HdmiControlService.java revision 08a1be81d7b597f858164fee6a4934264259b3ae
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
195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.DISABLED;
205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.ENABLED;
215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
2938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
307ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
3238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
3338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.database.ContentObserver;
35c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
367d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo;
3760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
39d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
40d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
416d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
42d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
4412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
45ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
46119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
47a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
485008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.net.Uri;
4942c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
5067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
5278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
53e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
5438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
5578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
5638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
577d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.os.SystemProperties;
585008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.os.UserHandle;
597ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
607d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.text.TextUtils;
612b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
620792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
648b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
6578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
664893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
68a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
707e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
73b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
74b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
7578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
76f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
770340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
7802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
79a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
800792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
810792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
820792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
830792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
840792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
87c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
8878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
89fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
90fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
91fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
92fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
93fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
94fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
95d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
96d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
97d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
98d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
99d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
100d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
101d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
102ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
1044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
1054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
1064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
1074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
108d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
109d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
110d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
111d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
11202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
11302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
11402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
11502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
11602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
11702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
11802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
11902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
12002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
12102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
12202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
12302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
12438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
12538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
12638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
12738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
12838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
12938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
13038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
13138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
13238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
13338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
13438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
13538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
13638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
13738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
13838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
13938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
14038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
14138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1420792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1430792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
14778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
14878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
14978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
15278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
15378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
15478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
15678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
15778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
15878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1594893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
16078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
16178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
16278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1654893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1694893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
173119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    // List of records for vendor command listener to handle the the caller killed in action.
174119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
175119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
176119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
177119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1789c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1799c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
184b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
18512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private IHdmiRecordListener mRecordListener;
186b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
187b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
18812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
189b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
19092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
19192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
19292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
19392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
19492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1954d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1964d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1984d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
1994d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2004d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
20108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    // Set to true while the input change by MHL is allowed.
20208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    @GuardedBy("mLock")
20308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    private boolean mMhlInputChangeEnabled;
20408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
205ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
206ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
207ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
208ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
209ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
210ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
211ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
212ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2134893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2165008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2175008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
2180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
2230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2282b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
22930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2302b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2312b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
23230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2332b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
23475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
23575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
23638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
23738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
23838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
239c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
24038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
24138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
24238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
24338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
244fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
245fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
246fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
247867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
248867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
249867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
2500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2527d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
2535008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
2540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2567d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
2577d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
2587d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
2597d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
2607d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
2617d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
2627d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
2637d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
2647d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
2657d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
2667d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
2677d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
2687d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
2697d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
2700792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2722f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
273c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
2747ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
2757ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
27608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
2778b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
278a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
2793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
280347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
2815008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
282347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
283a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
284a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
285fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
286a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
287a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
291e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2952b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        initPortInfo();
29675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
2978692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
29863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
29938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
30038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
30138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
30238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
30338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
30438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
30538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
3067ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
30738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
30825c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
30925c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
31025c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
31125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    private void onInitializeCecComplete() {
312fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
313fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
314fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
315fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
316fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
31725c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
3185008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
3195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            registerContentObserver();
32025c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
32125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
32225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
3235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
3255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
3265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
3275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
3285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
3295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
3305008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
3315008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
3325008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
3335008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        for (String s: settings) {
3345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
3355008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
3365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3375008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3385008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3395008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
3405008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
3415008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
3425008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3435008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3445008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
3455008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
3465008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
3475008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
3485008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
3495008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
3505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
3515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3525008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
3535008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoWakeup(enabled);
3545008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
3555008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
3575008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoDeviceOff(enabled);
3585008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
3595008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3605008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
36108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
3625008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
36408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    if (mMhlController != null) {
36508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                        mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
36608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    }
3675008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3685008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
3695008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3705008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3715008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3725008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
3735008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
3745008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3755008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3767ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
3777ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
3785008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
3797ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
3807ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
3817ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
3827ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
3835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
3845008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3855008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3865008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void unregisterSettingsObserver() {
3875008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
3880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
389e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
390fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
3915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
392fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        initializeLocalDevices(mLocalDevices, initiatedBy);
393a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
394a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
395a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
396fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeLocalDevices(final List<Integer> deviceTypes, final int initiatedBy) {
397a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
3993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
400fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        final int[] finished = new int[1];
4014b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo        clearLocalDevices();
4023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
4033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
4043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
4053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
4063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
4073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
4083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
409c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
4103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
4113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
412410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
413410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
414410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
415410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
4163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
4173ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
4183ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
4193ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
4203ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4224893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
423fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    if (deviceTypes.size() == ++finished[0]) {
424fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        onInitializeCecComplete();
425fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        notifyAddressAllocated(devices, initiatedBy);
4263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
4283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
4293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
432a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
433fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
434a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
4363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
4373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
438fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
4393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
4430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
444a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4452b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
446a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
4480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
4500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
4510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
4520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
4530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
4540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
4552b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
4562b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
4572b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
45830c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
45930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
4602b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
46130c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
46230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
4630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
46430c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
46530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
4660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
467f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        if (mMhlController == null) {
468f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
469f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            return;
470f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        } else {
471f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
472f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
473f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : mhlPortInfo) {
474f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (info.isMhlSupported()) {
475f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    mhlSupportedPorts.add(info.getId());
476f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
4770340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
4780340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
479f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            // Build HDMI port info list with CEC port info plus MHL supported flag.
480f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
481f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : cecPortInfo) {
482f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (mhlSupportedPorts.contains(info.getId())) {
483f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
484f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                            info.isCecSupported(), true, info.isArcSupported()));
485f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                } else {
486f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(info);
487f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
4882b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
489f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(result);
4902b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
4910340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
4920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
4940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
4950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
4960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
4970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
4980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
4990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
5002b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
5010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
503e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
504401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
505401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
506401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
507401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
508401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
509401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
510401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
511c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
512401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
513401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
514401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
515401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
516401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
517401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
518401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
519401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
520401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
521401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
522401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
523c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
5242b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
525401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
526401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
52709ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
5282b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
52909ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
53009ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
531401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
532e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
533e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
534e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
535e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
536e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
537e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
538e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
539e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
540e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
541e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
542e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
543e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
544e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
545e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
546e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
54767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
548e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
549c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
550c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
5513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
5523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
5543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
5553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
5583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
5593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
5613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
5623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
564a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
56561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
5660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
56779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
56879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
56979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
57079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
57179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
572a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
573a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
5743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
575092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
576092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
577092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
578092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
579092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
580092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
581092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
58260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
58360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
58460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
5852b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        int portId = mPortIdMap.get(physicalAddress);
5862b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
5872b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
58860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
58960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
59060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
59160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
59279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
59367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
59467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
59567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
59663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
59763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
59863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
59963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
60063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
60163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
60263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
60363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
60463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
60563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
60667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
607c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
608c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
609c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
610d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
611c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
612a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
613d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
614a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6155f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        if (mMessageValidator.isValid(command)) {
6165f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
6175f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
6185f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            Slog.e(TAG, "Invalid message type:" + command);
6195f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
6205f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
6215f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
6225f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
623d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
624d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
625a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
626d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
627a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6285f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
629c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
630c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
6317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
6327df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command) {
6337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
6347df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        sendMhlSubcommand(portId, command, null);
6357df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
6367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
6377df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
6387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command, SendMessageCallback callback) {
6397df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
6407df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        mMhlController.sendSubcommand(portId, command, callback);
6417df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
6427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
6436aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
6446aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
6456aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
6466aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
6476aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
6486aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
6496aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
6506aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
6516aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
6526aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
6536aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
6546aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
655a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
656a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
657a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
65875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
65975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
66075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
661092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
662092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
663092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
66479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
66579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
66660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
66760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
668a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
669092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
670a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
671092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
67279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
673c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
674092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
675092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
676092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
67760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
678c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
6793a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
6803a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
681092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
682a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
683a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
68467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
68567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
68667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
6878b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
68867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
68967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
690a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
69167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
69260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
69379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
69479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
69560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
69660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
69767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
69867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
69902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
70002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
70102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
70202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
70302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
7041de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
7050f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
70602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
7070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
70802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
709a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
7101de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
7111de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
712a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7131de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
7141de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
7150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
7160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
7170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
718c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
7190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
7200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
7210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
722c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
7230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
7240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
7250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
7260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
72702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
72802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
72960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
73060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
73160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
73260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
73360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
73479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
73579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
73679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
73779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
73879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
739b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
740b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
741b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
742b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
743b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
744b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
745b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
746b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
747b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
748b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
749b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
750b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
751b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
752b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
753b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
754b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
7553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
757ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
758ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
759ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
760ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
761ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
762ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
763410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
76442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
76542c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
76661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
7672b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
7682b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
7693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
7717df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
7727df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    boolean handleMhlSubcommand(int portId, HdmiMhlSubcommand message) {
7737df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
7747df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
7757df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
7767df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
7777df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            return device.handleSubcommand(message);
7787df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
7797df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        Slog.w(TAG, "No mhl device exists[portId:" + portId + ", message:" + message);
7807df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        return false;
7817df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
7827df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
7837df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
7847df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
7857df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
7867df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
7877df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice newDevice = new HdmiMhlLocalDevice(this, portId);
7887df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice oldDevice = mMhlController.addLocalDevice(newDevice);
7897df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
7907df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
7917df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
7927df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
7937df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
7947df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice device = mMhlController.removeLocalDevice(portId);
7957df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
7967df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
7977df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
7987df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
7997df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
8007df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8017df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8027df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8037df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8047df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlCbusModeChanged(int portId, int cbusmode) {
8057df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8067df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8077df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8087df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCbusMode(cbusmode);
8097df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8107df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for cbus mode change[portId:" + portId +
8117df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    ", cbusmode:" + cbusmode + "]");
8127df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8137df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8157df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8167df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlVbusOvercurrent(int portId, boolean on) {
8177df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8187df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8197df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8207df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.onVbusOvercurrentDetected(on);
8217df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8227df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for vbus overcurrent event[portId:" + portId + "]");
8237df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8247df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8257df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8267df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8277df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleCapabilityRegisterChanged(int portId, int adopterId, int deviceId) {
8287df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8297df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8307df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        // Hot plug event should be called before capability register change event.
8317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8327df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCapabilityRegister(adopterId, deviceId);
8337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8347df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for capability register change event[portId:"
8357df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
8367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8377df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
83978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
84078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
84178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
84278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
84378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
84478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
84578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
84678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
84778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
84878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
84978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
85078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
85178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
85278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
85378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
85478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
85578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
85678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
8576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
8586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
8596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
8616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
8626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
8636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
865ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
8666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
8676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
8686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
8696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
8706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
8716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
8726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
873ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
87438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
875ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
876ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
877ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
878ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
879ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
880ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
881ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
882ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
883ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
884ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
885ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
886ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
887ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
888ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
889119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
890119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
891119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
892119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
893119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
894119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
895119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
896119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
897119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
898119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
899119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
900119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
901119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
902119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
903119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
904119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
905119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
90612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
907b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
908b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
909b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
91012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                mRecordListener = null;
911b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
912b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
913b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
914b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
91578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
91678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
91778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
91878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
91978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
92078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
92178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
92278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
9230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
9240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
9250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
9260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
92778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
9280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
92978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
93078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
93178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
93261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
9337e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
9347e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
9357e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
9367e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
9377e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9387e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
9397e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
94061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
94161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
94261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
9437e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9447e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
94561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
94661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
9477e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
9487e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
9497e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
9507e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
9517e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
952a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
953a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
954a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
955a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
956a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
95772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
95872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
95972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
96072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
96179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
962a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
963a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
964c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
965a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
966a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
967a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
968a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
969a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
970a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
971a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
972a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
973a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
974a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
975a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
976a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
977a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
97872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
97972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
98072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
98172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
982a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
983a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
984a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
985c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
986a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
987a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
9888333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
989a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
990a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
991a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
992a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
993a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
994c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
995a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
996a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
997a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
998a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
999c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
1000c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    if (localDevice == null) {
1001c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                        Slog.w(TAG, "Local device not available");
1002a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1003a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1004c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    localDevice.sendKeyEvent(keyCode, isPressed);
1005a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1006a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1007a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1008a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1009a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
10107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
101178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
10167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
101878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
101978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
102078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
10217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
102278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
10277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
102978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
103078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
103178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
10327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
103378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
10387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
104078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
104178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
104278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
10437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
104478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
10467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
10477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
10487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
10497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
10507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
105178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
10526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
10536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
10546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
10556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
10566d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
10576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
1058ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
10596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
10606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
10616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
10626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
10636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
10646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
10656d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
10666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
10676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
10686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1069ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1070ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1071ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1072ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1073ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1074ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1075ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1076ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1077e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1078ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1079ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1080ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1081ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1082ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1083ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1084ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1085ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1086ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1087377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1088ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1089ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1090ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1091ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1092ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1093ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1094ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1095ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1096ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1097ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1098ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1099c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1100ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1101ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1102ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1103ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1104ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1105ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1106ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1107ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1108ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1109ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1110ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1111ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1112ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1113ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1114ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1115ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1116ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1117ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1118ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1119ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
112092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
112192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
11229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
11239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
11249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
11259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
112861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
11299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
11309c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
11319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
11329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
11339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
11349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
11359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
11369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
11379c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11399c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
114041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
114141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
114241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
114341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
114441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
114541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
114641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
114741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
114841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
114941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
115041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
115141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
115241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
115341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
115441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
115541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
115641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
115741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
115841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
115941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
116041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
116141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
116241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
116341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
116441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
116541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
116641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
116741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
116841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
116941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
117041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
117141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
117241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1173a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1174a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1175a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1176a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1177a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1178a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1179a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
118038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1181a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1182a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1183a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1184a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1185a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1186160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1187160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
11884d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
11894d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
11904d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
11914d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
11924d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
11934d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
11944d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1195119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1196119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1197119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1198119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1199119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1200119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1201119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1202119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1203119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1204119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1205119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
1206119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1207119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1208119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1209119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1210119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1211119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1212119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1213119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1214119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1215119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1216119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1217119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1218119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1219119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1220119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1221119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1222119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1223119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1224119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1225119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1226119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1227119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1228119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1229119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
123012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1231a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1232a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
123312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
123412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1235b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1236b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1237b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1238b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1239b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1240b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1241b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1242b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1243b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1244b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1245b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1246b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1247b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1248b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1249b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1250b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1251b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1252b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1253b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1254b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1255b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1256b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1257b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1258b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1259b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1260b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1261b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1262b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1263a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1264a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1265a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1266b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1267b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1268b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1269b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1270b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1271b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1272b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1273b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1274b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1275b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1276b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1277b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1278bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1279bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1280bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1281b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1282b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1283b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1284b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1285b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1286b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1287b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1288b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1289b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1290b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1291b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1292b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1293a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
129478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
129578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1296a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
129779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
129879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
129979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
13007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
13017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1302c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
13037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
13047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
130579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
130678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
130778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1308a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
130979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
131079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
131179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
13127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
13137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1314c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
13157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
13167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
131779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
131878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
131978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
132078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
132178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
132278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
132378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
132478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
132578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
132678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
132778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
132878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
132978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
133078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
133178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
133278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
133378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
133478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
133578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
133678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
133778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
133878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
133978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
134078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
134178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
134278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
134378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
134478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
134578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
13467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
13476d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
13484893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
13494893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
13504893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
13514893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
13524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
13534893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
13544893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
13556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
13564893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
13574893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
13584893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
13594893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
13604893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
136161daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
13624893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
13634893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
13644893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
136561daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang                    listener.onStatusChanged(device, status);
13664893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
13674893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
13686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
13696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
13706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
13716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
13726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1373ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1374ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1375ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1376ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1377ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1378ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1379ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1380ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1381ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1382ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1383ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1384ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1385ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1386ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1387ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1388ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1389ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1390ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1391ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1392ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1393ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1394ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1395ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1396ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1397ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1398ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1399ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1400ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1401ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
14029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
14039c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
14049c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
14059c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
14069c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
14079c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14099c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
14119c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
14129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
14139c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
14149c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
14159c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
14169c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
14179c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
14189c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
14199c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14209c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
14219c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
142461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
14259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
14269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
14279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
142872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    mInputChangeListener.onChanged(info);
14299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
14309c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
14319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
14329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
14359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
143612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1437b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
143812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord();
1439b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
144012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1441b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
144212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1443b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
144412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListener = listener;
1445b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1446b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1447b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1448b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1449b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
145012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
145112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
145212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    return mRecordListener.getOneTouchRecordSource(recorderAddress);
145312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
145412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1455b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1456b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1457b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1458b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1459b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1460b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
146112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
146212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
146312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
146412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
146512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onOneTouchRecordResult(result);
146612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
146712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
146812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
146912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
147012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
147112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
147212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
147312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
147412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
147512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
147612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
147712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onTimerRecordingResult(result);
147812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1479e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1480e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1481e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1482e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1483e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1484e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1485e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    void invokeClearTimerRecordingResult(int result) {
1486e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1487e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            if (mRecordListener != null) {
1488e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1489e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    mRecordListener.onClearTimerRecordingResult(result);
1490e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1491e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
149212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
149312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
149412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
149512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
149612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
14977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
14987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
14997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
15007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
15017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
15027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
15037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
150463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1505ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1506ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1507ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1508ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1509ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1510ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1511ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1512ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1513ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
15144893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
15154893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
151660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
151760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
15184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
151960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
152060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
152160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
152260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
15234893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
152460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
152560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
152660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
152760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
152860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
152960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1530e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1531e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
153279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
153361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
153479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
153579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1536e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1537e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1538e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1539e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
154079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1541c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
154261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
154360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1544a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1545a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1546a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1547a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
154892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
154992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
155092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
155192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
155292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
155392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
155438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
155538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
155638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
155738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
155838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
155938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1560c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1561c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
156238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
156338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
156438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1565c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1566c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
156738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
156838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
156938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1570c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
157138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
157238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
157338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
157438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
157538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1576fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
157738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
157838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
157938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
158038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
158138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
158238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
158338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
158438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
158538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
158638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
158738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
158838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
158938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
159038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
159138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
159238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
15932849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    void nap() {
15942849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
15952849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        pm.nap(SystemClock.uptimeMillis());
15962849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    }
15972849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo
159838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
159938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
160038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1601c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
160238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1603a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1604fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1605fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1606fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1607fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1608fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1609a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
161038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
161138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
161238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
161338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
161438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
161538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
161638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
161738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
161838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1619c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
16204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
16214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
16224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
16234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
16244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
16254fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
16264fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
16274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
16284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
16294b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
16304b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
16314b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
16324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
16334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
16344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
16354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
16364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
16374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
163838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
16394fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.disableDevice(mStandbyMessageReceived, callback);
164038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
16415008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        if (isTvDevice()) {
16425008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            unregisterSettingsObserver();
16435008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
164438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
164538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
164638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
16474fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
16484fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
16494fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
16504fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
16514fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
16524fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
16534fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
16544fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
16554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
16564fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
16574fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
165838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
16594fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
16604fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1661c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
166238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
166338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1664c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
166538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
16664fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
166738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
166838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
16695008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
167038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
16714d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1672119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1673119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1674119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1675119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1676119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1677119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1678119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1679119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1680119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1681119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1682119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1683119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1684119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1685119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1686119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1687119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1688119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1689119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1690119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1691119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1692119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1693119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1694119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1695119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1696119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1697119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1698119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1699119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1700119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
17014d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
17024d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
17034d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
17044d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
17054d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
17064d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
17074d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
17084d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
17094d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
17104d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
17114d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
17124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
17145008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setOption(int key, int value) {
17155008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
17165008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
17175008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
17185008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
17195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
17205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
17214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
17224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        int value = toInt(enabled);
17245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_ENABLE, value);
17254fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mMhlController != null) {
17265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mMhlController.setOption(OPTION_MHL_ENABLE, value);
17274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17294fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
17304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
17314fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1734fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
17354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
17364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
17374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
17384fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
17394fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
17404fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
17414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
17424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
17434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
17444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
1745867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
1746867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
1747867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
1748867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
1749867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
1750867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
175108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
175208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    void setMhlInputChangeEnabled(boolean enabled) {
175308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        if (mMhlController != null) {
175408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
175508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
175608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
175708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
175808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
175908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
176008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
176108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
176208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
176308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
176408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
176508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
176608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
17670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1768