HdmiControlService.java revision 87f22a2870ac363a5849a7252c1bd44ce2b809c2
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
19ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
20ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.DISABLED;
225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.ENABLED;
235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
3138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
327ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
3438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
3538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.database.ContentObserver;
37c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
387d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo;
3960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
41d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
42d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
44d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
4612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
47ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
48119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
49a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.net.Uri;
5142c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
5267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
5478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
55e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
5638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
5778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
5838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
597d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.os.SystemProperties;
605008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.os.UserHandle;
617ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
627d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.text.TextUtils;
632b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
640792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
668b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
6778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
684893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
690792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
70a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
727e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
740792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
75b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
76b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
7778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
78f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
790340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
8002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
81a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
820792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
830792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
840792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
89c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
9078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
91fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
92fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
93fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
94fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
95fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
96fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
97d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
98d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
99d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
100d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
101d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
102d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
103d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
104ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
1064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
1074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
1084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
1094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
110d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
111d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
112d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
113d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
11402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
11502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
11602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
11702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
11802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
11902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
12002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
12102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
12202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
12302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
12402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
12502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
12638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
12738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
12838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
12938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
13038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
13138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
13238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
13338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
13438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
13538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
13638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
13738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
13838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
13938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
14038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
14138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
14238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
14338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
14978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
15078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
15178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
15478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
15578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
15678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1574893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
15878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
15978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
16078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1614893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
16278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
16378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
16478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1656d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1674893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1714893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1736d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1746d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
175119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    // List of records for vendor command listener to handle the the caller killed in action.
176119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
177119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
178119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
179119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1849c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
186b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
18712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private IHdmiRecordListener mRecordListener;
188b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
189b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
19012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
191b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
19292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
19392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
19492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
19592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
19692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1984d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1994d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
2004d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
2014d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2024d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
20308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    // Set to true while the input change by MHL is allowed.
20408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    @GuardedBy("mLock")
20508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    private boolean mMhlInputChangeEnabled;
20608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
207ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @GuardedBy("mLock")
208ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> mMhlDevices;
209ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
210ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
211ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
212ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
213ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
214ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
215ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
216ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
217ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
2230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
2280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2332b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
23430c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2352b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2362b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
23730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2382b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
23975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
24075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
24138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
24238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
24338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
244c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
24538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
24638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
24738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
24838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
249fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
250fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
251fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
252867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
253867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
254867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
2550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2577d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
2585008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
2590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2617d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
2627d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
2637d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
2647d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
2657d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
2667d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
2677d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
2687d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
2697d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
2707d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
2717d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
2727d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
2737d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
2747d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
2750792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2772f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
278c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
2797ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
2807ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
28108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
2828b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
283a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
2843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
285347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
2865008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
287347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
288a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
289a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
290fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
291a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
292a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
296e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
3002b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        initPortInfo();
301ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        mMhlDevices = Collections.emptyList();
30275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
3038692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
30463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
30538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
30638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
30738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
30838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
30938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
31038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
31138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
3127ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
31338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
31425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
31525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
31625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
31725c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    private void onInitializeCecComplete() {
318fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
319fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
320fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
321fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
322fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
32325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
3245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
3255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            registerContentObserver();
32625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
32725c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
32825c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
3295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
3305008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
3315008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
3325008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
3335008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
3345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
3355008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
3365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
3375008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
3385691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        for (String s : settings) {
3395008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
3405008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
3415008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3425008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3435008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3445008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
3455008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
3465008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
3475008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3485008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3495008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
3505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
3515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
3525008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
3535008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
3545008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
3555008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
3565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3575008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
3585008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoWakeup(enabled);
3595008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
3605008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
3625008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoDeviceOff(enabled);
3635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
3645008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3655008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
36608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
3675008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3685008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
36908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    if (mMhlController != null) {
37008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                        mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
37108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    }
3725008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3735008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
3745008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3755008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3765008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3775008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
3785008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
3795008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3805008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3817ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
3827ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
3835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
3847ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
3857ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
3867ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
3877ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
3885008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
3895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3905008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void unregisterSettingsObserver() {
3925008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
3930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
394e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
395fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
3965008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
397fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        initializeLocalDevices(mLocalDevices, initiatedBy);
398a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
399a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
400a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
401fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeLocalDevices(final List<Integer> deviceTypes, final int initiatedBy) {
402a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
4043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
405fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        final int[] finished = new int[1];
4064b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo        clearLocalDevices();
4073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
4083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
4093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
4103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
4113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
4123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
4133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
414c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
4153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
4163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
417410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
418410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
419410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
420410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
4213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
4223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
4233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
4243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
4253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
428fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    if (deviceTypes.size() == ++finished[0]) {
429fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        onInitializeCecComplete();
430fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        notifyAddressAllocated(devices, initiatedBy);
4313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
4333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
4343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
437a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
438fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
439a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
4413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
4423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
443fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
4443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
4480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
449a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4502b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
451a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
4530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
4550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
4560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
4570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
4580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
4590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
4602b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
4612b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
4622b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
46330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
46430c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
4652b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
46630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
46730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
4680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
46930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
47030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
4710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
472f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        if (mMhlController == null) {
473f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
474f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            return;
475f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        } else {
476f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
477f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
478f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : mhlPortInfo) {
479f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (info.isMhlSupported()) {
480f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    mhlSupportedPorts.add(info.getId());
481f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
4820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
4830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
484f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            // Build HDMI port info list with CEC port info plus MHL supported flag.
485f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
486f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : cecPortInfo) {
487f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (mhlSupportedPorts.contains(info.getId())) {
488f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
489f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                            info.isCecSupported(), true, info.isArcSupported()));
490f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                } else {
491f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(info);
492f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
4932b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
494f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(result);
4952b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
4960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
4970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
4990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
5000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
5010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
5020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
5030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
5040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
5052b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
5060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
508e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
509401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
510401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
511401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
512401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
513401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
514401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
515401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
516c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
517401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
518401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
519401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
520401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
521401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
522401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
523401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
524401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
525401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
526401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
527401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
528c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
5292b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
530401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
531401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
53209ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
5332b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
53409ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
53509ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
536401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
537e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
538e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
539e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
540e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
541e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
542e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
543e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
544e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
545e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
546e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
547e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
548e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
549e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
550e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
551e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
55267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
553e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
554c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
555c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
5563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
5573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
5593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
5603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
5633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
5643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
5663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
5673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
569a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
57061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
5710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
57279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
57379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
57479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
57579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
5768960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        return tv.getCecDeviceInfo(logicalAddress);
577a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
578a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
5793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
580092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
581092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
582092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
583092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
584092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
585092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
586092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
58760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
58860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
58960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
5902b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        int portId = mPortIdMap.get(physicalAddress);
5912b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
5922b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
59360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
59460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
59560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
59660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
59779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
59867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
59967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
60067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
60163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
60263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
60363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
60463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
60563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
60663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
60763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
60863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
60963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
61063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
61167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
612c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
613c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
614c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
615d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
616c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
617a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
618d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
619a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6205f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        if (mMessageValidator.isValid(command)) {
6215f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
6225f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
6235f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            Slog.e(TAG, "Invalid message type:" + command);
6245f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
6255f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
6265f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
6275f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
628d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
629d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
630a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
631d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
632a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6335f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
634c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
635c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
6367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
6377df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command) {
6387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
6397df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        sendMhlSubcommand(portId, command, null);
6407df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
6417df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
6427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
6437df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command, SendMessageCallback callback) {
6447df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
6457df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        mMhlController.sendSubcommand(portId, command, callback);
6467df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
6477df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
6486aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
6496aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
6506aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
6516aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
6526aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
6536aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
6546aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
6556aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
6566aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
6576aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
6586aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
6596aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
660a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
661a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
662a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
66375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
66475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
66575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
666092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
667092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
668092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
66979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
67079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
67160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
67260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
673a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
674092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
675a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
676092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
67779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
678c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
679092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
680092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
681092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
68260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
683c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
6843a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
6853a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
686092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
687a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
688a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
68967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
69067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
69167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
692ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim     * @param portId hdmi port number where hot plug event issued.
69367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
69467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
695a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
696ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void onHotplug(int portId, boolean connected) {
69760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
69879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
699ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            device.onHotplug(portId, connected);
70060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
701ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
70267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
70367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
70402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
70502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
70602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
70702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
70802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
7091de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
7100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
71102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
7120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
71302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
714a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
7151de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
7161de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
717a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7181de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
7191de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
7200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
7210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
7220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
723c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
7240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
7250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
7260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
727c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
7280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
7290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
7300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
7310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
73202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
73302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
73460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
73560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
73660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
73760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
73860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
73979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
74079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
74179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
74279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
74379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
744b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
745b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
746b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
747b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
748b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
749b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
750b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
751b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
752b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
753b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
754b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
755b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
756b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
757b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
758b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
759b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
7603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
762ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
763ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
764ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
765ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
766ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
767ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
768410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
76942c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
77042c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
77161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
7722b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
7732b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
7743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
7767df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
7777df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    boolean handleMhlSubcommand(int portId, HdmiMhlSubcommand message) {
7787df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
7797df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
7807df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
7817df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
7827df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            return device.handleSubcommand(message);
7837df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
7847df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        Slog.w(TAG, "No mhl device exists[portId:" + portId + ", message:" + message);
7857df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        return false;
7867df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
7877df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
7887df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
7897df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
7907df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
7917df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
7927df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice newDevice = new HdmiMhlLocalDevice(this, portId);
7937df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice oldDevice = mMhlController.addLocalDevice(newDevice);
7947df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
7957df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
7967df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
7977df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
7987df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
7997df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice device = mMhlController.removeLocalDevice(portId);
8007df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
8017df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
802ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                // There is no explicit event for device removal unlike capability register event
803ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                // used for device addition . Hence we remove the device on hotplug event.
804ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
805ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                updateSafeMhlInput();
8067df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
8077df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
8087df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
8097df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
810ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
8117df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8127df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8137df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlCbusModeChanged(int portId, int cbusmode) {
8157df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8167df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8177df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8187df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCbusMode(cbusmode);
8197df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8207df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for cbus mode change[portId:" + portId +
8217df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    ", cbusmode:" + cbusmode + "]");
8227df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8237df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8247df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8257df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8267df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlVbusOvercurrent(int portId, boolean on) {
8277df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8287df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8297df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8307df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.onVbusOvercurrentDetected(on);
8317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8327df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for vbus overcurrent event[portId:" + portId + "]");
8337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8347df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8357df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
837ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void handleMhlCapabilityRegisterChanged(int portId, int adopterId, int deviceId) {
8387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8397df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
840ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
841ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        // Hotplug event should already have been called before capability register change event.
8427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8437df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCapabilityRegister(adopterId, deviceId);
844ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_ADD_DEVICE);
845ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            updateSafeMhlInput();
8467df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8477df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for capability register change event[portId:"
8487df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
8497df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8507df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8517df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
852ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @ServiceThreadOnly
853ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private void updateSafeMhlInput() {
854ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        assertRunOnServiceThread();
855ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<HdmiDeviceInfo> inputs = Collections.emptyList();
856ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        SparseArray<HdmiMhlLocalDevice> devices = mMhlController.getAllLocalDevices();
857ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        for (int i = 0; i < devices.size(); ++i) {
858ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiMhlLocalDevice device = devices.valueAt(i);
859ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiDeviceInfo info = device.getInfo();
860ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            if (info != null) {
861ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                if (inputs.isEmpty()) {
862ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                    inputs = new ArrayList<>();
863ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                }
864ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                inputs.add(device.getInfo());
865ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            }
866ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
867ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        synchronized (mLock) {
868ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            mMhlDevices = inputs;
869ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
870ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
871ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
872ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> getMhlDevicesLocked() {
873ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return mMhlDevices;
874ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
875ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
87678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
87778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
87878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
87978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
88078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
88178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
88278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
88378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
88478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
88578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
88678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
88778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
88878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
88978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
89078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
89178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
89278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
89378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
8946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
8956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
8966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
8986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
8996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
9016d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
902ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
9036d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
9046d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
9056d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
9066d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
9076d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
9096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
910ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
91138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
912ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
913ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
914ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
915ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
916ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
917ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
918ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
919ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
920ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
921ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
922ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
923ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
924ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
925ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
926119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
927119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
928119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
929119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
930119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
931119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
932119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
933119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
934119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
935119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
936119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
937119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
938119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
939119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
940119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
941119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
942119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
94312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
944b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
945b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
946b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
94712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                mRecordListener = null;
948b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
949b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
950b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
951b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
95278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
95378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
95478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
95578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
95678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
95778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
95878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
95978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
9600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
9610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
9620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
9630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
96478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
9650340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
96678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
96778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
96878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
96961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
9707e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
9717e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
9727e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
9737e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
9747e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9757e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
9767e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
97761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
97861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
97961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
9807e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9817e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
98261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
98361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
9847e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9857e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
9867e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
9877e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
9887e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
9898960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
990a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
991a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
992a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
993a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
99472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
99572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
99672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
99772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
99879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
999a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
1000a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1001c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1002a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
1003a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
100487f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    if (mMhlController != null) {
100587f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        HdmiMhlLocalDevice device = mMhlController.getLocalDeviceById(deviceId);
100687f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        if (device != null) {
100787f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            // Upon selecting MHL device, we send RAP[Content On] to wake up
100887f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            // the connected mobile device, start routing control to switch ports.
100987f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            // callback is handled by MHL action.
101087f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            device.turnOn(callback);
101187f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            tv.doManualPortSwitching(device.getInfo().getPortId(), null);
101287f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            return;
101387f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        }
101487f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    }
10158960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                    tv.deviceSelect(deviceId, callback);
1016a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
1017a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
1018a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
1019a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
1020a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
1021a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
1022a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1023a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1024a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1025a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
102672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
102772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
102872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
102972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
1030a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
1031a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
1032a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1033c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1034a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1035a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
10368333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
1037a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1038a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1039a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1040a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1041a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
1042c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
1043a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1044a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1045a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1046a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
10474612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mMhlController != null) {
10484612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(mActivePortId);
10494612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (device != null) {
10504612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            device.sendKeyEvent(keyCode, isPressed);
10514612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
10524612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
10534612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    }
10544612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mCecController != null) {
10554612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
10564612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (localDevice == null) {
10574612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            Slog.w(TAG, "Local device not available");
10584612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
10594612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
10604612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        localDevice.sendKeyEvent(keyCode, isPressed);
1061a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1062a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1063a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1064a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1065a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1066a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
10677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
106878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10697fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
10737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
107578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
107678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
107778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
10787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
107978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
10847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
108678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
108778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
108878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
10897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
109078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
10957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
109778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
109878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
109978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
110178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
11067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
110878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
11096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
11126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
11136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
11146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
1115ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
11166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
11176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
11186d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
11196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11206d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11216d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11226d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
11236d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
11246d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
11256d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1126ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1127ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1128ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1129ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1130ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1131ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1132ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1133ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1134e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1135ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1136ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1137ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1138ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1139ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1140ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1141ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1142ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1143ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1144377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1145ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1146ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1147ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1148ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1149ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1150ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1151ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1152ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1153ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1154ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1155ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1156c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1157ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1158ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1159ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1160ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1161ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1162ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1163ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1164ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1165ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1166ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1167ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1168ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1169ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1170ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1171ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1172ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1173ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1174ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1175ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1176ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
117792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
117892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
11799c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
11809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
11819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
11829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11849c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
118561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
11869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
11879c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
11889c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
11899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1190ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            synchronized (mLock) {
1191ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                List<HdmiDeviceInfo> cecDevices = (tv == null)
1192ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1193ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        : tv.getSafeExternalInputsLocked();
1194ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
11959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
11969c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11979c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
119941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
120041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
120141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
120241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
120341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
120441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
120541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
120641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
120741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
120841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
120941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
121041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
121141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
121241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
121341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
121441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
121541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
121641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
121741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
121841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
121941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
122041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
122141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
122241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
122341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
122441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
122541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
122641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
122741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
122841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
122941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
123041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
123141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1232a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1233a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1234a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1235a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1236a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1237a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1238a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
123938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1240a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1241a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1242a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1243a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1244a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1245160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1246160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
12474d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
12484d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
12494d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
12504d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
12514d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
12524d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
12534d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1254119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1255119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1256119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1257119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1258119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1259119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1260119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1261119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1262119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1263119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1264119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
1265119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1266119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1267119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1268119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1269119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1270119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1271119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1272119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1273119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1274119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1275119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1276119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1277119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1278119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1279119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1280119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1281119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1282119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1283119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1284119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1285119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1286119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1287119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1288119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
128912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1290a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1291a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
129212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
129312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1294b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1295b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1296b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1297b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1298b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1299b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1300b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1301b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1302b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1303b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1304b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1305b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1306b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1307b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1308b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1309b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1310b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1311b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1312b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1313b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1314b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1315b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1316b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1317b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1318b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1319b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1320b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1321b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1322a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1323a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1324a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1325b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1326b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1327b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1328b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1329b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1330b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1331b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1332b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1333b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1334b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1335b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1336b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1337bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1338bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1339bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1340b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1341b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1342b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1343b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1344b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1345b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1346b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1347b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1348b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1349b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1350b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1351b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1352a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
135378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
135478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1355a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
135679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
135779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
135879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
13597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
13607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1361c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
13627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
13637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
136479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
136578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
136678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1367a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
136879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
136979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
137079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
13717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
13727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1373c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
13747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
13757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
137679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
137778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
137878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
137978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
138078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
138178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
138278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
138378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
138478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
138578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
138678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
138778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
138878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
138978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
139078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
139178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
139278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
139378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
139478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
139578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
139678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
139778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
139878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
139978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
140078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
140178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
140278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
140378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
140478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
14057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
14066d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
14074893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
14084893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
14094893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
14104893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
14114893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
14124893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
14134893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
14146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
14154893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
14164893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
14174893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
14184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
14194893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
142061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
14214893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
14224893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
14234893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
142461daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    listener.onStatusChanged(device, status);
14254893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
14264893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
14276d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
14286d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
14296d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
14306d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
14316d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1432ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1433ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1434ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1435ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1436ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1437ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1438ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1439ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1440ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1441ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1442ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1443ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1444ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1445ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1446ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1447ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1448ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1449ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1450ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1451ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1452ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1453ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1454ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1455ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1456ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1457ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1458ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1459ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1460ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
14619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
14629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
14639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
14649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
14659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
14669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14689c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14699c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
14709c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
14719c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
14729c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
14739c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
14749c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
14759c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
14769c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
14779c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
14789c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14799c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
14809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
148361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
14849c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
14859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
14869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
148772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    mInputChangeListener.onChanged(info);
14889c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
14899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
14909c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
14919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14929c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
149512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1496b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
149712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord();
1498b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
149912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1500b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
150112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1502b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
150312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListener = listener;
1504b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1505b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1506b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1507b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1508b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
150912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
151012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
151112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    return mRecordListener.getOneTouchRecordSource(recorderAddress);
151212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
151312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1514b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1515b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1516b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1517b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1518b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1519b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
152012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
152112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
152212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
152312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
152412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onOneTouchRecordResult(result);
152512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
152612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
152712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
152812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
152912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
153012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
153112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
153212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
153312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
153412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
153512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
153612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onTimerRecordingResult(result);
153712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1538e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1539e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1540e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1541e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1542e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1543e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1544e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    void invokeClearTimerRecordingResult(int result) {
1545e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1546e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            if (mRecordListener != null) {
1547e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1548e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    mRecordListener.onClearTimerRecordingResult(result);
1549e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1550e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
155112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
155212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
155312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
155412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
155512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
15567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
15577fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
15587fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
15597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
15607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
15617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
15627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
156363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1564ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1565ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1566ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1567ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1568ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1569ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1570ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1571ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1572ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
15734893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
15744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
157560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
157660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
15774893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
157860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
157960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
158060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
158160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
15824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
158360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
158460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
158560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
158660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
158760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
158860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1589e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1590e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
159179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
159261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
159379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
159479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1595e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1596e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1597e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1598e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
159979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1600c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
160161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
160260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1603a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1604a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1605a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1606a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
160792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
160892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
160992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
161092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
161192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
161292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
161338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
161438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
161538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
161638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
161738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
161838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1619c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1620c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
162138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
162238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
162338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1624c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1625c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
162638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
162738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
162838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1629c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
163038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
163138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
163238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
163338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
163438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1635fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
163638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
163738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
163838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
163938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
164038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
164138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
164238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
164338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
164438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
164538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
164638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1647c12035cd40d01b032013f515cb509e6c8791cf65Jeff Brown        pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
164838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
164938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
165038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
165138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
16522849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    void nap() {
16532849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
16542849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        pm.nap(SystemClock.uptimeMillis());
16552849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    }
16562849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo
165738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
165838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
165938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1660c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
166138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1662a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1663fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1664fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1665fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1666fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1667fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1668a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
166938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
167038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
167138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
167238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
167338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
167438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
167538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
167638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
167738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1678c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
16794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
16804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
16814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
16824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
16834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
16844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
16854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
16864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
16874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
16884b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
16894b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
16904b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
16914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
16924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
16934fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
16944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
16954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
16964fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
169738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
16984fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.disableDevice(mStandbyMessageReceived, callback);
169938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
17005008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        if (isTvDevice()) {
17015008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            unregisterSettingsObserver();
17025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
170338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
170438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
170538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
17064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
17074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
17084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
17094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
17104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
17124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
17134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
17144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
17164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
171738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
17184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
17194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1720c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
172138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
172238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1723c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
172438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
17254fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
172638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
172738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
17285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
172938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
17304d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1731119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1732119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1733119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1734119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1735119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1736119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1737119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1738119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1739119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1740119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1741119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1742119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1743119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1744119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1745119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1746119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1747119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1748119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1749119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1750119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1751119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1752119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1753119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1754119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1755119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1756119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1757119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1758119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1759119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
17604d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
17614d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
17624d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
17634d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
17644d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
17654d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
17664d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
17674d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
17684d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
17694d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
17704d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
17714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
17735008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setOption(int key, int value) {
17745008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
17755008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
17765008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
17775008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
17785008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
17795008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
17804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
17814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17825008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        int value = toInt(enabled);
17835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_ENABLE, value);
17844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mMhlController != null) {
17855008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mMhlController.setOption(OPTION_MHL_ENABLE, value);
17864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
17894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
17904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1793fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
17944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
17954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
17964fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
17974fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
17984fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
17994fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
18004fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
18014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
18024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
18034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
1804867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
1805867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
1806867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
1807867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
1808867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
1809867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
181008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
181108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    void setMhlInputChangeEnabled(boolean enabled) {
181208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        if (mMhlController != null) {
181308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
181408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
181508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
181608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
181708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
181808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
181908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
182008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
182108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
182208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
182308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
182408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
182508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
18260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1827