HdmiControlService.java revision e9cf1583c74fd03977c1ecb14520663710f14439
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;
286d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
29d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
30ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
3142c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
3267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
3478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
35e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
3678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
398b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
4078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
414893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
420792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
43a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
4678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
4802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
49a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
5878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
5978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
60d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
61d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
62d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
63d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
64d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
65d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
66d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
67ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
68ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
69ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
70ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
74d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
7502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
7602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
7702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
7802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
7902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
8102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
8202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
9278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
9378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
9478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
9778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
9978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1004893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
10178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
10278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1044893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
10578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
10678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
10778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1104893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1144893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1156d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
118ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
119ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
120ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
121ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
122ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
123ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
124ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
125ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1264893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
1370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
1380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
1410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
1420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
1430792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1472f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
148e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
1498b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
1503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
1513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
152a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
1530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
1540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
156e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
1570792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
1580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
1590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mPortInfo = initPortInfo();
1618692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
16263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
16363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
16463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
1650792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
166e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
167a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private void initializeLocalDevices(final List<Integer> deviceTypes) {
169a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
1703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
1713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
1723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
17313c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim        mCecController.clearLogicalAddress();
1743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
1753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
1763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
1773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
1783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
1793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
1803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
1813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
1823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
1833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
1843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
1853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
1863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
1873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
1883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
1893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
1913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1924893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
1930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
1943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
1953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
1973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
1983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
1993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
201a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
203a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
2053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
2063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
20713c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim            device.handleAddressAllocated(address);
2083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2110340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
2120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
213a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> initPortInfo() {
215a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
2170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
2190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
2210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
2220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
2240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return Collections.emptyList();
2250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mMhlController != null) {
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // TODO: Implement plumbing logic to get MHL port information.
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mhlPortInfo = mMhlController.getPortInfos();
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // Use the id (port number) to find the matched info between CEC and MHL to combine them
2340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
2350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
2360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int i = 0; i < cecPortInfo.length; ++i) {
2370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            HdmiPortInfo cec = cecPortInfo[i];
2380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int id = cec.getId();
2390340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            boolean mhlInfoFound = false;
2400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (HdmiPortInfo mhl : mhlPortInfo) {
2410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                if (id == mhl.getId()) {
2420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
2430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
2440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    mhlInfoFound = true;
2450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    break;
2460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                }
2470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (!mhlInfoFound) {
2490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                result.add(cec);
2500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(result);
2540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
2570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
2580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
2590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
2600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
2610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
2620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
2630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // mPortInfo is an unmodifiable list and the only reference to its inner list.
2640340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // No lock is necessary.
2650340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
2660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (portId == info.getId()) {
2670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                return info;
2680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return null;
2710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
273e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
274401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
275401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
276401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
277401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
278401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
279401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
280401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
28160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
282401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
283401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
284401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
285401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
286401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
287401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
288401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
289401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
290401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
291401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
292401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
293401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK;
294401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
295401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            if (portAddress == info.getAddress()) {
296401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim                return info.getId();
297401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            }
298401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
29960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return HdmiConstants.INVALID_PORT_ID;
300401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
301401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
302401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
303e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
304e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
305e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
306e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
307e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
308e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
309e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
310e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
311e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
312e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
313e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
314e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
315e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
316e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
317e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
31867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
319e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
320c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
321c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
3223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
3233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
3253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
3263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
3293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
3303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
3323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
3333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
335a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
336a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
3370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
33879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
33979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
34079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
34179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
34279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
343a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
344a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
3453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
346092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
352092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
35360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
35460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
35560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
35660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        for (HdmiPortInfo portInfo : mPortInfo) {
35760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
35860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                    && portInfo.isArcSupported()) {
35960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return true;
36060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
36160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
36260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
36360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
36460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
36579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
36667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
36767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
36867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
36963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
37063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
37163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
37263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
37363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
37463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
37563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
37663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
37763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
37863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
37967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
380c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
381c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
382c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
383d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
384c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
385a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
386d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
387a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
388d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
389d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
390d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
391a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
392d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
393a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
394d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
395c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
396c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
397a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
398a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
399a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
400092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
401092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
402092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
40379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
40479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
40560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
40660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
407a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
408092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
409a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
410092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
41179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
41279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
413092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
414092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
415092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
41660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
41760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        Slog.w(TAG, "Unhandled cec command:" + message);
418092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
419a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
420a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
42167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
42267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
42367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
4248b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
42567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
42667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
427a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
42867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
42960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
43079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
43179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
43260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
43360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
43467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
43567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
43602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
43702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
43802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
43902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
44002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
4410f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
44202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
4430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
44402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
445a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4460f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
447a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4480f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
4490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
4510f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
4523ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
4530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
4540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
4550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4563ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
4570f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
4580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
4590f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
46102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
46202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
46360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
46460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
46560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
46660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
46760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
46879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
46979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
47079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
47179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
47279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
47379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // TODO: Hook up with AudioManager.
4743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
476ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
477ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
478ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
479ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
480ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
481ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
4823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
48342c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
48442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
4853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
4863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
4873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
48978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
49078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
49178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
49278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
49378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
49578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
49678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
49778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
49978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
50078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
50178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
50278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
50378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
50478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
50578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
50678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
5076d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
5086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
5096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
5116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
5126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
515ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
5166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
5176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
5186d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
5196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
5206d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5216d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
5226d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
523ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
524ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        private IHdmiSystemAudioModeChangeListener mListener;
525ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
526ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
527ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
528ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
529ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
530ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
531ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
532ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
533ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
534ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
535ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
536ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
537ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
538ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
53978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
54078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
54178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
54278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
54378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
54478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
54578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
54678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
5480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
5490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
5500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
55178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
5520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
55378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
55478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
55578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
556a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
557a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
558a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
559a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
560a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
56179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
562a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
563a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
564a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
565a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
566a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
567a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
568a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
569a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
570a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
571a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
572a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
573a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
574a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
575a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
576a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
577a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
578a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
579a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
580a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
581a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
582a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
583a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
584a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    tv.portSelect(portId, callback);
585a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
586a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
587a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
588a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
589a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
590a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
591a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
592a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
593a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
594a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
595a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    // TODO: sendKeyEvent is for TV device only for now. Allow other
596a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    //       local devices of different types to use this as well.
597a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
598a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
599a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
600a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
601a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
602a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    tv.sendKeyEvent(keyCode, isPressed);
603a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
604a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
605a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
606a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
607a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
6087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
60978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
6147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
61678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
61778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
61878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6197fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
62078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
6257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
62778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
62878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
62978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
63178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
6367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
63878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
63978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
64078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
64278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
6477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
64978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
6506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
6526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
6536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
6546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
6556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
656ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
6576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
6586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
6596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
6606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
6636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
6646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
6656d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
6666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
667ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
668ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
669ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
670ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
671ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
672ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
673ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
674ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
675e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
676ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
677ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
678ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
679ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
680ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
681ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
682ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
683ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
684ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
685ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return tv.getSystemAudioMode();
686ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
687ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
688ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
689ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
690ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
691ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
692ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
693ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
694ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
695ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
696ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
697ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
698ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
699ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
700ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
701ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
702ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
703ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
704ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
705ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
706ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
707ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
708ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
709ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
710ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
711ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
712ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
713ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
714ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
715ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
716ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
717ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
71878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
71978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
720a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
72179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
72279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
72379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
7247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
7257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
7267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
7277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
7287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
72979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
73078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
73178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
732a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
73379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
73479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
73579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
7367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
7377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
7387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
7397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
7407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
74179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
74278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
74378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
74478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
74578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
74678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
74778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
74878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
74978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
75078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
75178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
75378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
75478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
75578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
75778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
75878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
75978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
76078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
76178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
76278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
76378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
76478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
76578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
76678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
76778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
76878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
76978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
7707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
7716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
7724893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
7734893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
7744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
7754893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
7764893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
7774893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
7784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
7796d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
7804893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
7814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
7824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
7834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
7844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
7854893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
7864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
7874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
7884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
7894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
7904893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
7914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
7926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
7936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
7946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
7956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
7966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
797ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
798ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
799ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
800ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
801ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
802ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
803ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
804ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
805ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
806ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
807ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
808ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
809ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
810ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
811ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
812ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
813ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
814ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
815ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
816ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
817ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
818ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
819ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
820ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
821ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
822ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
823ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
824ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
825ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
8267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
8277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
8287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
8297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
8307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
8317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
8327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
83363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
834ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
835ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
836ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
837ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
838ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
839ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
840ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
841ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
842ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
8434893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
8444893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
84560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
84660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
8474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
84860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
84960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
85060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
85160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
8524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
85360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
85460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
85560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
85660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
85760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
85860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
859e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
860e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
86160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
86260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
86360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
86460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
86560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
86679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
86779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
86879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
86979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
87079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
87179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
87260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
8730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
874