HdmiControlService.java revision 7c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8
10792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/*
20792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Copyright (C) 2014 The Android Open Source Project
30792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
40792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License");
50792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * you may not use this file except in compliance with the License.
60792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * You may obtain a copy of the License at
70792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
80792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *      http://www.apache.org/licenses/LICENSE-2.0
90792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Unless required by applicable law or agreed to in writing, software
110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS,
120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * See the License for the specific language governing permissions and
140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * limitations under the License.
150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpackage com.android.server.hdmi;
180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
19ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
20ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.DISABLED;
225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.ENABLED;
235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
3138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
327ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
3438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
3538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.database.ContentObserver;
37c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
387d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo;
3960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
41d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
42d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
44d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
46f424932cfb1b16b01a37500d09e295912700a51dJungshik Jangimport android.hardware.hdmi.IHdmiMhlScratchpadCommandListener;
4712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
48ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
49119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
50a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.net.Uri;
5242c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
5367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
5578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
56e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
5738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
5878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
5938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
607d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.os.SystemProperties;
615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.os.UserHandle;
627ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
637d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.text.TextUtils;
642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
650792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
678b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
6878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
694893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
70959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter;
710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
72a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
747e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
77b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
78b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
79959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.FileDescriptor;
80959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.PrintWriter;
8178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
82f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
851ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heoimport java.util.Locale;
86a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
94c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
9578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
96fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
97fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
98fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
99fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
100fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
101fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
102d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
103d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
104d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
105d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
106d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
107d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
108d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
109ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
1114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
1124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
1134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
1144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
115d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
116d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
117d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
118d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
11902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
12002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
12102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
12202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
12302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
12402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
12502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
12602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
12702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
12802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
12902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
13002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
1311ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
132f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        @ServiceThreadOnly
13338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
13438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
135f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang            assertRunOnServiceThread();
13638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
13738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
13838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
13938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
14038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
14138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
14238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
14338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
14438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
14538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
14638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
1471ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                case Intent.ACTION_CONFIGURATION_CHANGED:
1481ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    String language = Locale.getDefault().getISO3Language();
1491ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    if (!mLanguage.equals(language)) {
1501ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                        onLanguageChanged(language);
1511ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    }
1521ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    break;
15338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
15438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
15538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
15638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1570792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1610792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
16278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
16378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
16478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1650340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
16778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
168dbe6b45545dcd28e6aaf46986ed694196eb94de0Jungshik Jang    private final HdmiLogger mSpamSafeLogger = new HdmiLogger(TAG);
169dbe6b45545dcd28e6aaf46986ed694196eb94de0Jungshik Jang
17078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1714893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
17278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
17378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
17478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
175f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for device event listener to handle the caller killed in action.
1764893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1786d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1796d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
180f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for vendor command listener to handle the caller killed in action.
181119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
182119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
183119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
184119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1879c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
188b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
18912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
190b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
19192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
19292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
19392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
19492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
19592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1964d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1984d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1994d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
2004d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2014d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
202ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
203ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
204ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
205ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2064893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2095008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2105008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
211f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private final HdmiControlBroadcastReceiver
212f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
213f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2212b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
22230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2232b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2242b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
22530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2262b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
227e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Map from port ID to HdmiDeviceInfo.
228e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
229e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
23075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
23175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
23238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
233c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
23438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
23538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
2361ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private String mLanguage = Locale.getDefault().getISO3Language();
2371ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2381ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
23938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
24038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
241fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
242fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
243fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
244867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
245867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
246867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
247f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Set to true while the input change by MHL is allowed.
248f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
249f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private boolean mMhlInputChangeEnabled;
250f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
251f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // List of records for MHL Scratchpad command listener to handle the caller killed in action.
252f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
253f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private final ArrayList<HdmiMhlScratchpadCommandListenerRecord>
254f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mScratchpadCommandListenerRecords = new ArrayList<>();
255f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
256f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
257f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private List<HdmiDeviceInfo> mMhlDevices;
258f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
259f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @Nullable
260f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private HdmiMhlController mMhlController;
261f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
262f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Last input port before switching to the MHL port. Should switch back to this port
263f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // when the mobile device sends the request one touch play with off.
264e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Gets invalidated if we go to other port/input.
265e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
266e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private int mLastInputMhl = Constants.INVALID_PORT_ID;
267e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2680792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2690792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2707d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
2715008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
2720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2747d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
2757d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
2767d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
2777d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
2787d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
2797d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
2807d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
2817d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
2827d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
2837d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
2847d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
2857d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
2867d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
2877d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
2880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2902f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
291c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
2927ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
2937ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
29408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
2958b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
296a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
2973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
298347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
2995008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
300347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
301a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
302a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
303fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
304a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
305a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
3060792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
3070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
3080792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
309e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
310f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (!mMhlController.isReady()) {
3110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
3120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
313ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        mMhlDevices = Collections.emptyList();
314f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
315f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        initPortInfo();
31675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
3178692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
31863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
31938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
320f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mCecController != null) {
32138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
32238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
32338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
3241ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
3251ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
32638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
3277ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
32838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
32925c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
33025c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
33125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
33225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    private void onInitializeCecComplete() {
333fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
334fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
335fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
336fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
337fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
33825c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
3395008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
3405008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            registerContentObserver();
34125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
34225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
34325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
3445008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
3455008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
3465008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
3475008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
3485008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
3495008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
3505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
3515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
3525008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
3535691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        for (String s : settings) {
3545008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
3555008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
3565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3575008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3585008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3595008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
3605008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
3615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
3625008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
364f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        // onChange is set up to run in service thread.
3655008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
3665008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
3675008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
3685008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
3695008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
3705008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
3715008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
3725008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3735008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
3745008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoWakeup(enabled);
375350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                    setCecOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
3765008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3775008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
3785008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoDeviceOff(enabled);
3795008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
3805008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3815008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
38208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
3835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3845008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
385f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
3865008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
3875008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
3885008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3905008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
3925008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
3935008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3945008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3957ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
3967ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
3975008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
3987ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
3997ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
4007ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
4017ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
4025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
4035008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4045008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4055008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void unregisterSettingsObserver() {
4065008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
4070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
408e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
409fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
4105008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
411fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        initializeLocalDevices(mLocalDevices, initiatedBy);
412a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
413a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
414a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
415fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeLocalDevices(final List<Integer> deviceTypes, final int initiatedBy) {
416a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4173ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
4183ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
419fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        final int[] finished = new int[1];
4204b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo        clearLocalDevices();
4213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
4223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
4233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
4243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
4253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
4263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
4273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
428c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
4293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
4303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
431410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
432410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
433410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
434410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
4353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
4363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
4373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
4383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
4393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4414893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
442fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    if (deviceTypes.size() == ++finished[0]) {
443fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        onInitializeCecComplete();
444fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        notifyAddressAllocated(devices, initiatedBy);
4453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
4473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
4483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
451a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
452fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
453a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
4553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
4563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
457fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
4583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
4620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
463a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
4642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
465a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
4660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
4670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
4690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
4700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
4710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
4720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
4730340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
4742b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
4752b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
4762b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
47730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
47830c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
479e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
4802b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
48130c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
48230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
483e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
4840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
48530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
48630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
487e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
4880340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
489f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
490f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
491f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : mhlPortInfo) {
492f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (info.isMhlSupported()) {
493f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                mhlSupportedPorts.add(info.getId());
4940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
495f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
4960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
497f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
498f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // cec port info if we do not have have port that supports MHL.
499f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mhlSupportedPorts.isEmpty()) {
500f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
501f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            return;
502f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
503f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
504f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
505f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (mhlSupportedPorts.contains(info.getId())) {
506f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
507f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        info.isCecSupported(), true, info.isArcSupported()));
508f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            } else {
509f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(info);
5102b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
5112b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
512f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mPortInfo = Collections.unmodifiableList(result);
5130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
5152738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    List<HdmiPortInfo> getPortInfo() {
5162738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang        return mPortInfo;
5172738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    }
5182738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang
5190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
5200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
5210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
5220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
5230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
5240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
5250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
5262b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
5270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
529e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
530401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
531401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
532401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
533401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
534401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
535401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
536401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
537c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
538401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
539401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
540401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
541401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
542401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
543401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
544401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
545401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
546401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
547401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
548401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
549c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
5502b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
551401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
552401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
55309ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
5542b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
55509ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
55609ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
557401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
558e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
559e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
560e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
561e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
562e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
563e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
564e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
565e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
566e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
567e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
568e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
569e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
570e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
571e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
572e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
57367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
574e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
575c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
576c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
5773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
5783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
5803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
5813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
5843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
5853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
5863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
5873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
5883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
590a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
59161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
5920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
59379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
59479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
59579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
59679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
5978960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        return tv.getCecDeviceInfo(logicalAddress);
598a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
599a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
6003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
601092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
602092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
603092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
604092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
605092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
606092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
607092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
60860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
60960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
61060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
611339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        int portId = pathToPortId(physicalAddress);
6122b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
6132b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
61460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
61560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
61660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
61760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
61879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
61967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
62067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
62167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
62263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
62363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
62463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
62563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
62663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
62763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
62863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
62963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
63063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
63163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
63267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
633c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
634c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
635c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
636d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
637c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
638a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
639d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
640a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6415f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        if (mMessageValidator.isValid(command)) {
6425f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
6435f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
644dbe6b45545dcd28e6aaf46986ed694196eb94de0Jungshik Jang            mSpamSafeLogger.error("Invalid message type:" + command);
6455f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
6465f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
6475f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
6485f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
649d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
650d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
651a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
652d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
653a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6545f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
655c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
656c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
6576aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
6586aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
6596aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
6606aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
6616aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
6626aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
6636aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
6646aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
6656aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
6666aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
6676aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
6686aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
669a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
670a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
671a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
67275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
67375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
67475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
675092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
676092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
677092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
67879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
67979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
68060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
68160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
682a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
683092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
684a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
685092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
68679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
687c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
688092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
689092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
690092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
69160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
692c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
693dbe6b45545dcd28e6aaf46986ed694196eb94de0Jungshik Jang            mSpamSafeLogger.warning("Unhandled cec command:" + message);
6943a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
695092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
696a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
697a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
69867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
69967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
70067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
701ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim     * @param portId hdmi port number where hot plug event issued.
70267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
70367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
704a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
705ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void onHotplug(int portId, boolean connected) {
70660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
70779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
708ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            device.onHotplug(portId, connected);
70960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
710ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
71167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
71267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
71302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
71402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
71502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
71602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
71702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
7181de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
7190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
72002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
7210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
72202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
723a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
7241de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
7251de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
726a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7271de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
7281de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
7290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
7300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
7310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
732c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
7330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
7340f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
7350f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
736c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
7370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
7380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
7390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
7400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
74102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
74202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
74360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
74460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
74560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
74660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
74760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
74879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
74979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
75079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
75179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
75279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
753b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
754b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
755b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
756b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
757b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
758b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
759b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
760b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
761b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
762b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
763b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
764b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
765b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
766b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
767b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
768b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
7693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
771ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
772f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
773f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (SystemAudioModeChangeListenerRecord record :
774f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mSystemAudioModeChangeListenerRecords) {
775f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeSystemAudioModeChangeLocked(record.mListener, enabled);
776f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
777ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
778ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
779ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
780410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
78142c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
78242c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
78361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
7842b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
7852b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
7863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
7887df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
789f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command) {
790f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        assertRunOnServiceThread();
791f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        sendMhlSubcommand(portId, command, null);
792f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
793f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
794f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @ServiceThreadOnly
795f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    void sendMhlSubcommand(int portId, HdmiMhlSubcommand command, SendMessageCallback callback) {
796f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        assertRunOnServiceThread();
797f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.sendSubcommand(portId, command, callback);
798f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
799f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
800f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @ServiceThreadOnly
8017df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    boolean handleMhlSubcommand(int portId, HdmiMhlSubcommand message) {
8027df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8037df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8047df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8057df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8067df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            return device.handleSubcommand(message);
8077df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8087df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        Slog.w(TAG, "No mhl device exists[portId:" + portId + ", message:" + message);
8097df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        return false;
8107df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8117df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8127df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8137df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
8147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8157df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
8167df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice newDevice = new HdmiMhlLocalDevice(this, portId);
8177df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice oldDevice = mMhlController.addLocalDevice(newDevice);
8187df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
8197df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
8207df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
8217df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
8227df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8237df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            HdmiMhlLocalDevice device = mMhlController.removeLocalDevice(portId);
8247df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
8257df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
826ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                // There is no explicit event for device removal unlike capability register event
827ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                // used for device addition . Hence we remove the device on hotplug event.
8287c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                HdmiDeviceInfo deviceInfo = device.getInfo();
8297c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                if (deviceInfo != null) {
8307c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                    invokeDeviceEventListeners(deviceInfo, DEVICE_EVENT_REMOVE_DEVICE);
8317c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                    updateSafeMhlInput();
8327c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                }
8337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
8347df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
8357df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
8367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
837ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
8387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8397df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8407df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8417df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlCbusModeChanged(int portId, int cbusmode) {
8427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8437df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8447df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8457df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCbusMode(cbusmode);
8467df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8477df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for cbus mode change[portId:" + portId +
8487df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    ", cbusmode:" + cbusmode + "]");
8497df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8507df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8517df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8527df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8537df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlVbusOvercurrent(int portId, boolean on) {
8547df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8557df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
8567df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8577df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.onVbusOvercurrentDetected(on);
8587df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8597df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for vbus overcurrent event[portId:" + portId + "]");
8607df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8617df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8627df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8637df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
864ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void handleMhlCapabilityRegisterChanged(int portId, int adopterId, int deviceId) {
8657df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8667df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
867ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
868ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        // Hotplug event should already have been called before capability register change event.
8697df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
8707df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            device.setCapabilityRegister(adopterId, deviceId);
871ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_ADD_DEVICE);
872ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            updateSafeMhlInput();
8737df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8747df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            Slog.w(TAG, "No mhl device exists for capability register change event[portId:"
8757df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
8767df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8777df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8787df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
879ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @ServiceThreadOnly
880ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private void updateSafeMhlInput() {
881ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        assertRunOnServiceThread();
882ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<HdmiDeviceInfo> inputs = Collections.emptyList();
883ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        SparseArray<HdmiMhlLocalDevice> devices = mMhlController.getAllLocalDevices();
884ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        for (int i = 0; i < devices.size(); ++i) {
885ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiMhlLocalDevice device = devices.valueAt(i);
886ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiDeviceInfo info = device.getInfo();
887ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            if (info != null) {
888ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                if (inputs.isEmpty()) {
889ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                    inputs = new ArrayList<>();
890ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                }
891ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                inputs.add(device.getInfo());
892ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            }
893ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
894ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        synchronized (mLock) {
895ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            mMhlDevices = inputs;
896ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
897ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
898ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
899ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> getMhlDevicesLocked() {
900ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return mMhlDevices;
901ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
902ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
903f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private class HdmiMhlScratchpadCommandListenerRecord implements IBinder.DeathRecipient {
904f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        private final IHdmiMhlScratchpadCommandListener mListener;
905f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
906f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        public HdmiMhlScratchpadCommandListenerRecord(IHdmiMhlScratchpadCommandListener listener) {
907f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mListener = listener;
908f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
909f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
910f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        @Override
911f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        public void binderDied() {
912f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mScratchpadCommandListenerRecords.remove(this);
913f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
914f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
915f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
91678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
91778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
91878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
91978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
92078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
92178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
92278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
92378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
92478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
92578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
92678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
92778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
92878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
92978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
93078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
93178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
93278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9336d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
9346d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
9356d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
9366d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
9376d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
9386d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9396d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
9406d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
941ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
9426d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
9436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
9446d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
9456d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9466d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
9476d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
948ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
94938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
950ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
951ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
952ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
953ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
954ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
955ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
956ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
957ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
958ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
959ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
960ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
961ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
962ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
963119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
964119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
965119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
966119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
967119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
968119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
969119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
970119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
971119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
972119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
973119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
974119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
975119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
976119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
977119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
978119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
979119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
98012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
981f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiRecordListener mListener;
982f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
983f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public HdmiRecordListenerRecord(IHdmiRecordListener listener) {
984f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
985f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
986f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
987b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
988b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
989b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
990f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mRecordListenerRecord = null;
991b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
992b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
993b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
994b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
99578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
99678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
99778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
99878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
99978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
100078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
100178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
100278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
10040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
10050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
10060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
100778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
10080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
100978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
101078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
101178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
101261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
10137e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
10147e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
10157e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
10167e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
10177e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10187e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
10197e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
102061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
102161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
102261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
10237e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10247e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
102561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
102661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
10277e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10287e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
10297e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
10307e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
10317e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
10328960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
1033a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
1034a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
1035a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
1036a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
103772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
103872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
103972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
104072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
104179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1042a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
1043a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1044c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1045a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
1046a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
1047f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    HdmiMhlLocalDevice device = mMhlController.getLocalDeviceById(deviceId);
1048f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1049f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        if (device.getPortId() == tv.getActivePortId()) {
1050f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
105187f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            return;
105287f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        }
1053f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // Upon selecting MHL device, we send RAP[Content On] to wake up
1054f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // the connected mobile device, start routing control to switch ports.
1055f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // callback is handled by MHL action.
1056f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.turnOn(callback);
10577c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                        tv.doManualPortSwitching(device.getPortId(), null);
1058f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
105987f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    }
10608960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                    tv.deviceSelect(deviceId, callback);
1061a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
1062a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
1063a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
1064a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
1065a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
1066a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
1067a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1068a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1069a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1070a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
107172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
107272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
107372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
107472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
1075a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
1076a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
1077a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1078c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1079a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1080a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
10818333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
1082a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1083a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1084a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1085a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1086a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
1087c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
1088a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1089a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1090a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1091a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
1092f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    HdmiMhlLocalDevice device = mMhlController.getLocalDevice(mActivePortId);
1093f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1094f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.sendKeyEvent(keyCode, isPressed);
1095f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
10964612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    }
10974612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mCecController != null) {
10984612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
10994612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (localDevice == null) {
11004612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            Slog.w(TAG, "Local device not available");
11014612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
11024612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
11034612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        localDevice.sendKeyEvent(keyCode, isPressed);
1104a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1105a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1106a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1107a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1108a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1109a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
11107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
111178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
11167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
111878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
111978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
112078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
112278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
11277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
112978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
113078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
113178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
113378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1134f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addHotplugEventListener(listener);
113578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
113678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
113778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
113978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1140f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.removeHotplugEventListener(listener);
114178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
11426d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11446d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
11456d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
1146f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addDeviceEventListener(listener);
11476d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11486d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11496d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
11516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
11522738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang            return HdmiControlService.this.getPortInfo();
11536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1154ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1155ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1156ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1157ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1158ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1159ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1160ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1161ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1162e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1163ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1164ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1165ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1166ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1167ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1168ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1169ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1170ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1171ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1172377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1173ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1174ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1175ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1176ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1177ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1178ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1179ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1180ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1181ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1182ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1183ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1184c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1185ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1186ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1187ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1188ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1189ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1190ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1191ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1192ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1193ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1194ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1195ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1196ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1197ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1198ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1199ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1200ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1201ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1202ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1203ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1204ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
120592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
120692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
12079c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
12089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12099c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
12109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12119c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
121361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
12149c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12159c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
12169c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
12179c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1218ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            synchronized (mLock) {
1219ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                List<HdmiDeviceInfo> cecDevices = (tv == null)
1220ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1221ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        : tv.getSafeExternalInputsLocked();
1222ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
12239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
122741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
122841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
122941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
123041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
123141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
123241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
123341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
123441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
123541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
123641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
123741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
123841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
123941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
124041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
124141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
124241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
124341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
124441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
124541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
124641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
124741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
124841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
124941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
125041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
125141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
125241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
125341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
125441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
125541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
125641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
125741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
125841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
125941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1260a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1261a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1262a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1263a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1264a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1265a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1266a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
126738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1268a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1269a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1270a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1271a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1272a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1273160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1274160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
12754d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
12764d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
12774d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
12784d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
12794d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
12804d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
12814d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1282119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1283119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1284119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1285119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1286119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1287f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1288119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1289119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1290119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1291119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1292119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1293119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1294119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1295119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1296119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1297119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1298119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1299119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1300119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1301119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1302119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1303119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1304119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1305119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1306119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1307119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1308119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1309119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1310119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1311119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
131212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1313a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1314a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
131512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
131612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1317b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1318b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1319b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1320b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1321b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1322b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1323b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1324b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1325b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1326b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1327b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1328b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1329b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1330b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1331b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1332b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1333b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1334b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1335b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1336b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1337b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1338b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1339b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1340b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1341b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1342b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1343b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1344b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1345a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1346a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1347a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1348b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1349b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1350b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1351b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1352b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1353b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1354b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1355b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1356b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1357b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1358b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1359b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1360bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1361bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1362bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1363b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1364b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1365b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1366b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1367b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1368b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1369b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1370b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1371b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1372b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1373b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1374b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1375a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1376f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1377f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1378f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public void sendScratchpadCommand(final int portId, final int offset, final int length,
1379f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                final byte[] data) {
1380f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1381f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            runOnServiceThread(new Runnable() {
1382f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                @Override
1383f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                public void run() {
1384f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (!isControlEnabled()) {
1385f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Hdmi control is disabled.");
1386f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return ;
1387f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
1388f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
1389f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (device == null) {
1390f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Invalid port id:" + portId);
1391f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return;
1392f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
1393f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mMhlController.sendScratchpadCommand(portId, offset, length, data);
1394f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1395f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            });
1396f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1397f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1398f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1399f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public void addHdmiMhlScratchpadCommandListener(
1400f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                IHdmiMhlScratchpadCommandListener listener) {
1401f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1402f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addHdmiMhlScratchpadCommandListener(listener);
1403f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1404959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1405959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        @Override
1406959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1407959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1408959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1409959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1410959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
1411959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mProhibitMode: " + mProhibitMode);
1412959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            if (mCecController != null) {
1413959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("mCecController: ");
1414959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.increaseIndent();
1415959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                mCecController.dump(pw);
1416959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.decreaseIndent();
1417959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1418959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPortInfo: ");
1419959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.increaseIndent();
1420959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
1421959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("- " + hdmiPortInfo);
1422959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1423959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.decreaseIndent();
1424959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPowerStatus: " + mPowerStatus);
1425959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
142678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
142778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1428a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
142979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
143079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
143179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
14327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
14337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1434c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
14357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
14367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
143779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
143878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
143978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1440a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
144179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
144279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
144379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
14447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
14457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1446c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
14477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
14487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
144979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
145078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
145178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
145278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
145378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
145478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
145578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
145678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
145778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
145878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
145978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
146078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
146178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
146278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
146378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
146478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
146578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
146678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
146778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
146878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
146978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
147078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
147178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
147278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
147378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
147478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
147578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
14767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
14776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
14784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
14794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
14804893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
14814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
14824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
14834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
14844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
14856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
14864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
14874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
14884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
14894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
149061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
14914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
1492f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
14934893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
1494f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onStatusChanged(device, status);
14954893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
14964893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
14976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
14986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
14996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
15006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
15016d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1502ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1503ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1504ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1505ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1506ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1507ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1508ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1509ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1510ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1511ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1512ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1513ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1514ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1515ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1516ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1517ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1518ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1519ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1520ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1521ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1522ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1523ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1524ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1525ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1526ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1527ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1528ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
15299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
1530f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiInputChangeListener mListener;
1531f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1532f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public InputChangeListenerRecord(IHdmiInputChangeListener listener) {
1533f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1534f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1535f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
15369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
15379c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
15389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
1539f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mInputChangeListenerRecord = null;
15409c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
15419c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
15429c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
15439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
15449c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
15459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1546f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mInputChangeListenerRecord = new InputChangeListenerRecord(listener);
15479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
15489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
15499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
15509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
15519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
15529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
15539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
15549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
15559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
155661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
15579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1558f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mInputChangeListenerRecord != null) {
15599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
1560f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mInputChangeListenerRecord.mListener.onChanged(info);
15619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
15629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
15639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
15649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
15659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
15669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
15679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
156812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1569b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1570f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord(listener);
1571b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
157212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1573b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
157412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1575b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1576b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1577b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1578b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1579b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1580b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1581f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
158212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1583f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress);
158412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
158512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1586b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1587b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1588b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1589b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1590b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1591b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
159212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
159312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1594f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
159512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1596f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mRecordListenerRecord.mListener.onOneTouchRecordResult(result);
159712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
159812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
159912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
160012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
160112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
160212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
160312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
160412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
160512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1606f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
160712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1608f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mRecordListenerRecord.mListener.onTimerRecordingResult(result);
160912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1610e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1611e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1612e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1613e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1614e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1615e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1616e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    void invokeClearTimerRecordingResult(int result) {
1617e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1618f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
1619e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1620f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mRecordListenerRecord.mListener.onClearTimerRecordingResult(result);
1621e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1622e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
162312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
162412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
162512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
162612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
162712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
16287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
16297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
16307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
16317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
16327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
16337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
16347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
163563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1636f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener,
1637ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1638ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1639ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1640ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1641ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1642ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1643ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1644ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
16454893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
16464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
164760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
1648f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
1649f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeHotplugEventListenerLocked(record.mListener, event);
165060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
165160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
165260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
165360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
16544893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
165560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
165660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
165760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
165860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
165960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
166060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1661e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1662e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
166379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
166461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
166579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
166679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1667e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1668e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1669e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1670e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
167179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1672c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
167361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
167460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1675a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1676a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1677a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1678a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
167992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
168092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
168192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
168292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
168392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
168492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
168538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1686f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
168738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
1688f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
168938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
169038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
169138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1692f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
169338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1694f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1695c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1696c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
169738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
169838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1699f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
170038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1701f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1702c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1703c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
170438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
170538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1706f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
170738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1708f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1709c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
171038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
171138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
171238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
171338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
171438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1715fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
171638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
171738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
171838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
171938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
172038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
172138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
172238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
172338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
172438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
172538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
172638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1727c12035cd40d01b032013f515cb509e6c8791cf65Jeff Brown        pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
172838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
172938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
173038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
173138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
173238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
173338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
173438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1735c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
173638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1737a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1738fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1739fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1740fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1741fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1742fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1743a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
174438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
174538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
174638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
174738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
174838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
174938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
175038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
175138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
175238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1753c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
17544fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17554fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
17564fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
17574fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
17584fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
17594fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
17604fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
17614fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
17624fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
17634b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
17644b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
17654b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
17664fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
17674fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
17684fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
17694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
17704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
17711ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
17721ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private void onLanguageChanged(String language) {
17731ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        assertRunOnServiceThread();
17741ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        mLanguage = language;
17751ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
17761ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        if (isTvDevice()) {
17771ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            tv().broadcastMenuLanguage(language);
17781ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        }
17791ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    }
17801ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
1781f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
1782f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    String getLanguage() {
1783f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1784f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        return mLanguage;
1785f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    }
1786f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang
17874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
1788350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang        if (mCecController != null) {
1789350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
1790350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                device.disableDevice(mStandbyMessageReceived, callback);
1791350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            }
1792350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            if (isTvDevice()) {
1793350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                unregisterSettingsObserver();
1794350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            }
179538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1796350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang
1797f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.clearAllLocalDevices();
179838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
179938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
180038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
18014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
18024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
18034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
18044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
18054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
18064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
18074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
18084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
18094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
18114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
181238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
18134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
18144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1815c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
181638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
181738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1818c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
181938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
18204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
182138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
182238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
18235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
182438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
18254d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1826119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1827119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1828119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1829119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1830119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1831119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1832119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1833119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1834119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1835119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1836119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1837119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1838119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1839119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1840119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1841119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1842119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1843119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1844119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1845119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1846119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1847119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1848119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1849119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1850119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1851119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1852119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1853119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1854119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1855f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private void addHdmiMhlScratchpadCommandListener(IHdmiMhlScratchpadCommandListener listener) {
1856f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        HdmiMhlScratchpadCommandListenerRecord record =
1857f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                new HdmiMhlScratchpadCommandListenerRecord(listener);
1858f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        try {
1859f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            listener.asBinder().linkToDeath(record, 0);
1860f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        } catch (RemoteException e) {
1861f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            Slog.w(TAG, "Listener already died.");
1862f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            return;
1863f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1864f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1865f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1866f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mScratchpadCommandListenerRecords.add(record);
1867f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1868f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
1869f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1870f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    void invokeScratchpadCommandListeners(int portId, int offest, int length, byte[] data) {
1871f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1872f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (HdmiMhlScratchpadCommandListenerRecord record :
1873f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mScratchpadCommandListenerRecords) {
1874f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                try {
1875f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onReceived(portId, offest, length, data);
1876f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                } catch (RemoteException e) {
1877f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    Slog.e(TAG, "Failed to notify scratchpad command", e);
1878f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1879f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
1880f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1881f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
1882f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
18834d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
18844d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
18854d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
18864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
18874d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
18884d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
18894d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
18904d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
18914d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
18924d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
18934d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
18944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
1896350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang    void setCecOption(int key, int value) {
18975008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
18985008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
18995008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
19005008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
19015008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
19025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
19034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
19044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19055008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        int value = toInt(enabled);
19065008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_ENABLE, value);
1907f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.setOption(OPTION_MHL_ENABLE, value);
19084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
19104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
19114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
19124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1914fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
19154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
19164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
19174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
19184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
19194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
19204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
19214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
19224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
19234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
19244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
1925867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
1926867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
1927867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
1928867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
1929867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
1930e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1931e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // Resets last input for MHL, which stays valid only after the MHL device was selected,
1932e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // and no further switching is done.
1933e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        setLastInputForMhl(Constants.INVALID_PORT_ID);
1934e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
1935e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1936e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
1937e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void setLastInputForMhl(int portId) {
1938e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
1939e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mLastInputMhl = portId;
1940e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
1941e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1942e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
1943e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    int getLastInputForMhl() {
1944e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
1945e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        return mLastInputMhl;
1946e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
1947e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1948e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    /**
1949e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * Performs input change, routing control for MHL device.
1950e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     *
1951e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param portId MHL port, or the last port to go back to if {@code contentOn} is false
1952e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param contentOn {@code true} if RAP data is content on; otherwise false
1953e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     */
1954e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
1955e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void changeInputForMhl(int portId, boolean contentOn) {
1956e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
1957e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
1958e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
1959e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            @Override
1960e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            public void onComplete(int result) throws RemoteException {
1961e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // Keep the last input to switch back later when RAP[ContentOff] is received.
1962e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // This effectively sets the port to invalid one if the switching is for
1963e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // RAP[ContentOff].
1964e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                setLastInputForMhl(lastInput);
1965e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            }
1966e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        });
1967e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1968e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // MHL device is always directly connected to the port. Update the active port ID to avoid
1969e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // unnecessary post-routing control task.
1970e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().setActivePortId(portId);
1971e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
1972e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // The port is either the MHL-enabled port where the mobile device is connected, or
1973f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // the last port to go back to when turnoff command is received. Note that the last port
1974e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // may not be the MHL-enabled one. In this case the device info to be passed to
1975e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // input change listener should be the one describing the corresponding HDMI port.
1976e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        HdmiMhlLocalDevice device = mMhlController.getLocalDevice(portId);
1977e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        HdmiDeviceInfo info = (device != null && device.getInfo() != null)
1978e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                ? device.getInfo()
1979e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                : mPortDeviceMap.get(portId);
1980e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        invokeInputChangeListener(info);
1981867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
198208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
1983e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim   void setMhlInputChangeEnabled(boolean enabled) {
1984f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim       mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
198508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
198608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
198708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
198808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
198908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
199008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
199108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
199208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
199308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
199408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
199508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
1996339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang
1997339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    @ServiceThreadOnly
1998339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    void displayOsd(int messageId) {
1999339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        assertRunOnServiceThread();
2000339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
2001339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
2002339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
2003339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                HdmiControlService.PERMISSION);
2004339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    }
20050792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
2006