HdmiControlService.java revision 8333571bd5e0a08773a1679964f8d96227af3356
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;
31a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
3242c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
3367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
3578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
36e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
3778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
408b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
4178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
424893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
430792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
44a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
4778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
4902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
50a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
570792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
5978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
6078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
61d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
62d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
63d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
64d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
65d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
66d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
67d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
68ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
69ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
70ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
71ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
74d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
75d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
7602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
7702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
7802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
7902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
8002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
8202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
8702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
9378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
9478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
9578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
9878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
10078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1014893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
10278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
10378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1054893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
10678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
10778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
10878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1114893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1154893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1186d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
119ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
120ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
121ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
122ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
123ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
124ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
125ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
126ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
1380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
1390340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
1420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
1430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1482f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
149e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
1508b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
1513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
1523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
153a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
1540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
1550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
157e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
1580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
1590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
1600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mPortInfo = initPortInfo();
1628692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
16363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
16463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
16563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
1660792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
167e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
168a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
1690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private void initializeLocalDevices(final List<Integer> deviceTypes) {
170a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
1713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
1723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
1733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
17413c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim        mCecController.clearLogicalAddress();
1753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
1763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
1773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
1783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
1793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
1803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
1813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
1823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
1833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
1843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
1853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
1863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
1873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
1883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
1893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
1903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
1923ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1934893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
1940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
1953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
1963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
1983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
1993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
202a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
204a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
2063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
2073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
20813c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim            device.handleAddressAllocated(address);
2093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
2130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
214a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> initPortInfo() {
216a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
2180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
2210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
2220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
2230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
2250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return Collections.emptyList();
2260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mMhlController != null) {
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // TODO: Implement plumbing logic to get MHL port information.
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mhlPortInfo = mMhlController.getPortInfos();
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // Use the id (port number) to find the matched info between CEC and MHL to combine them
2350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
2360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
2370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int i = 0; i < cecPortInfo.length; ++i) {
2380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            HdmiPortInfo cec = cecPortInfo[i];
2390340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int id = cec.getId();
2400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            boolean mhlInfoFound = false;
2410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (HdmiPortInfo mhl : mhlPortInfo) {
2420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                if (id == mhl.getId()) {
2430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
2440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
2450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    mhlInfoFound = true;
2460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    break;
2470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                }
2480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (!mhlInfoFound) {
2500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                result.add(cec);
2510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(result);
2550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
2580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
2590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
2600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
2610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
2620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
2630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
2640340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // mPortInfo is an unmodifiable list and the only reference to its inner list.
2650340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // No lock is necessary.
2660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
2670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (portId == info.getId()) {
2680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                return info;
2690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
2700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return null;
2720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
2730340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
274e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
275401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
276401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
277401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
278401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
279401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
280401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
281401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
28260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
283401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
284401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
285401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
286401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
287401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
288401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
289401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
290401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
291401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
292401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
293401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
294401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK;
295401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
296401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            if (portAddress == info.getAddress()) {
297401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim                return info.getId();
298401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            }
299401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
30060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return HdmiConstants.INVALID_PORT_ID;
301401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
302401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
303401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
304e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
305e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
306e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
307e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
308e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
309e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
310e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
311e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
312e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
313e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
314e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
315e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
316e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
317e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
318e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
31967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
320e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
321c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
322c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
3233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
3243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
3263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
3273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
3303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
3313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
3333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
3343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
336a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
337a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
3380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
33979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
34079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
34179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
34279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
34379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
344a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
345a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
3463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
347092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
348092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
352092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
353092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
35460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
35560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
35660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
35760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        for (HdmiPortInfo portInfo : mPortInfo) {
35860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
35960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                    && portInfo.isArcSupported()) {
36060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return true;
36160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
36260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
36360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
36460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
36560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
36679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
36767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
36867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
36967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
37063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
37163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
37263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
37363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
37463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
37563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
37663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
37763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
37863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
37963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
38067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
381c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
382c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
383c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
384d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
385c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
386a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
387d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
388a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
389d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
390d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
391d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
392a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
393d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
394a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
395d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
396c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
397c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
398a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
399a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
400a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
401092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
402092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
403092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
40479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
40579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
40660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
40760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
408a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
409092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
410a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
411092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
41279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
41379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
414092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
415092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
416092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
41760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
41860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        Slog.w(TAG, "Unhandled cec command:" + message);
419092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
420a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
421a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
42267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
42367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
42467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
4258b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
42667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
42767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
428a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
42967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
43060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
43179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
43279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
43360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
43460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
43567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
43667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
43702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
43802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
43902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
44002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
44102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
4420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
44302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
4440f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
44502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
446a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4470f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
448a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
4500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4510f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
4520f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
4533ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
4540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
4550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
4560f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4573ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
4580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
4590f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
4600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4610f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
46202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
46302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
46460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
46560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
46660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
46760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
46860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
46979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
47079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
47179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
47279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
47379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
47479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // TODO: Hook up with AudioManager.
4753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
477ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
478ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
479ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
480ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
481ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
482ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
4833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
48442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
48542c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
4863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
4873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
4883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
49078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
49178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
49278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
49378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
49478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
49678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
49778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
49878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
49978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
50078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
50178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
50278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
50378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
50478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
50578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
50678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
50778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
5086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
5096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
5106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
5126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
5136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5156d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
516ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
5176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
5186d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
5196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
5206d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
5216d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5226d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
5236d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
524ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
525ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        private IHdmiSystemAudioModeChangeListener mListener;
526ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
527ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
528ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
529ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
530ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
531ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
532ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
533ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
534ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
535ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
536ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
537ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
538ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
539ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
54078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
54178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
54278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
54378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
54478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
54578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
54678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
54778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
5490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
5500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
5510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
55278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
5530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
55478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
55578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
55678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
557a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
558a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
559a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
560a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
561a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
56279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
563a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
564a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
565a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
566a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
567a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
568a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
569a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
570a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
571a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
572a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
573a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
574a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
575a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
576a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
577a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
578a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
579a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
580a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
581a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
582a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
583a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
584a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
5858333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
586a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
587a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
588a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
589a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
590a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
591a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
592a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
593a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
594a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
595a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
596a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    // TODO: sendKeyEvent is for TV device only for now. Allow other
597a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    //       local devices of different types to use this as well.
598a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
599a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
600a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
601a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
602a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
603a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    tv.sendKeyEvent(keyCode, isPressed);
604a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
605a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
606a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
607a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
608a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
6097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
61078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
6157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
61778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
61878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
61978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
62178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
6267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
62878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
62978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
63078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
63278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
6377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
63978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
64078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
64178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
64378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
6487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
65078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
6516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
6536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
6546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
6556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
6566d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
657ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
6586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
6596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
6606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
6616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
6646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
6656d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
6666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
6676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
668ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
669ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
670ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
671ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
672ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
673ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
674ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
675ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
676e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
677ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
678ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
679ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
680ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
681ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
682ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
683ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
684ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
685ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
686ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return tv.getSystemAudioMode();
687ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
688ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
689ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
690ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
691ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
692ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
693ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
694ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
695ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
696ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
697ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
698ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
699ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
700ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
701ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
702ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
703ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
704ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
705ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
706ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
707ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
708ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
709ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
710ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
711ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
712ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
713ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
714ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
715ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
716ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
717ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
718ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
71978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
72078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
721a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
72279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
72379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
72479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
7257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
7267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
7277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
7287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
7297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
73079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
73178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
73278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
733a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
73479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
73579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
73679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
7377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
7387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
7397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
7407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
7417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
74279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
74378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
74478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
74578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
74678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
74778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
74878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
74978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
75078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
75178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
75278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
75478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
75578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
75678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
75878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
75978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
76078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
76178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
76278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
76378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
76478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
76578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
76678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
76778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
76878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
76978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
77078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
7717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
7726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
7734893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
7744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
7754893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
7764893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
7774893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
7784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
7794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
7806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
7814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
7824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
7834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
7844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
7854893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
7864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
7874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
7884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
7894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
7904893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
7914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
7924893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
7936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
7946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
7956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
7966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
7976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
798ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
799ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
800ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
801ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
802ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
803ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
804ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
805ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
806ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
807ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
808ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
809ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
810ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
811ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
812ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
813ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
814ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
815ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
816ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
817ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
818ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
819ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
820ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
821ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
822ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
823ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
824ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
825ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
826ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
8277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
8287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
8297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
8307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
8317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
8327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
8337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
83463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
835ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
836ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
837ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
838ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
839ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
840ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
841ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
842ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
843ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
8444893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
8454893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
84660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
84760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
8484893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
84960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
85060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
85160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
85260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
8534893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
85460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
85560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
85660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
85760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
85860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
85960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
860e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
861e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
86260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
86360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
86460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
86560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
86660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
86779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
86879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
86979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
87079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
87179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
87279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
87360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
874a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
875a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
876a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
877a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
8780792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
879