HdmiControlService.java revision 7fa3a66470d2133796defd14a0600578758882ac
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;
46b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kimimport android.hardware.hdmi.IHdmiMhlVendorCommandListener;
4712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
48ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
49119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
50a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
517fa3a66470d2133796defd14a0600578758882acJinsuk Kimimport android.media.tv.TvInputManager;
527fa3a66470d2133796defd14a0600578758882acJinsuk Kimimport android.media.tv.TvInputManager.TvInputCallback;
535008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.net.Uri;
5442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
5567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
5778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
58e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
5938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
6078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
6138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
627d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.os.SystemProperties;
635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.os.UserHandle;
647ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
657d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.text.TextUtils;
662b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
698b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
7078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
714893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
72959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter;
730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
74a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
767e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
780792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
79b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
80b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
81959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.FileDescriptor;
82959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.PrintWriter;
8378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
84f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
850340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
871ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heoimport java.util.Locale;
88a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
955fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim    private final Locale HONG_KONG = new Locale("zh", "HK");
965fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim    private final Locale MACAU = new Locale("zh", "MO");
970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
98c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
9978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
100fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
101fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
102fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
103fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
104fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
105b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    static final int INITIATED_BY_HOTPLUG = 4;
106fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
107d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
108d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
109d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
110d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
111d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
112d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
113d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
114ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
1164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
1174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
1184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
1194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
120d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
121d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
122d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
123d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
12402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
12502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
12602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
12702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
12802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
12902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
13002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
13102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
13202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
13302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
13402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
13502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
1361ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
137f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        @ServiceThreadOnly
13838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
13938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
140f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang            assertRunOnServiceThread();
14138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
14238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
14338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
14438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
14538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
14638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
14738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
14838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
14938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
15038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
15138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
1521ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                case Intent.ACTION_CONFIGURATION_CHANGED:
1535fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                    String language = getMenuLanguage();
1541ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    if (!mLanguage.equals(language)) {
1551ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                        onLanguageChanged(language);
1561ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    }
1571ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    break;
15838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
15938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1605fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim
1615fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        private String getMenuLanguage() {
1625fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            Locale locale = Locale.getDefault();
1635fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
1645fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Android always returns "zho" for all Chinese variants.
1655fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Use "bibliographic" code defined in CEC639-2 for traditional
1665fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Chinese used in Taiwan/Hong Kong/Macau.
1675fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return "chi";
1685fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            } else {
1695fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return locale.getISO3Language();
1705fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            }
1715fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        }
17238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
17338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1740792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1750792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1770792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1780792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
17978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
18078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
18178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
18478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
18578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
18778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
18878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
18978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
190f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for device event listener to handle the caller killed in action.
1914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
195f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for vendor command listener to handle the caller killed in action.
196119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
197119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
198119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
199119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
2009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
2019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
2029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
203b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
20412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
205b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
20692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
20792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
20892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
20992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
21092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
2114d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
2124d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
2134d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
2144d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
2154d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2164d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
217ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
218ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
219ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
220ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2214893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
226f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private final HdmiControlBroadcastReceiver
227f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
228f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2362b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
23730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2382b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2392b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
24030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2412b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
242e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Map from port ID to HdmiDeviceInfo.
243e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
244e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
24575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
24675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
24738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
248c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
24938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
25038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
2511ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private String mLanguage = Locale.getDefault().getISO3Language();
2521ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2531ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
25438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
25538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
256fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
257fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
258fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
259867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
260867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
261867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
262f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Set to true while the input change by MHL is allowed.
263f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
264f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private boolean mMhlInputChangeEnabled;
265f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
266b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    // List of records for MHL Vendor command listener to handle the caller killed in action.
267f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
268b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private final ArrayList<HdmiMhlVendorCommandListenerRecord>
269b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords = new ArrayList<>();
270f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
271f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
272f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private List<HdmiDeviceInfo> mMhlDevices;
273f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
274f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @Nullable
275781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim    private HdmiMhlControllerStub mMhlController;
276f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
2777fa3a66470d2133796defd14a0600578758882acJinsuk Kim    @Nullable
2787fa3a66470d2133796defd14a0600578758882acJinsuk Kim    private TvInputManager mTvInputManager;
2797fa3a66470d2133796defd14a0600578758882acJinsuk Kim
280f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Last input port before switching to the MHL port. Should switch back to this port
281f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // when the mobile device sends the request one touch play with off.
282e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Gets invalidated if we go to other port/input.
283e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
284e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private int mLastInputMhl = Constants.INVALID_PORT_ID;
285e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2887d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
2895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
2900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2927d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
2937d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
2947d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
2957d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
2967d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
2977d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
2987d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
2997d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
3007d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
3017d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
3027d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
3037d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
3047d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
3057d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
3060792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
3070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
3082f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
309c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
3107ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
3117ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
31208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
3138b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
314a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
3153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
316347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
3175008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
318347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
319a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
320a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
321fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
322a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
323a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
3240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
32508f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            return;
3260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
3270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
328781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim        mMhlController = HdmiMhlControllerStub.create(this);
329f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (!mMhlController.isReady()) {
3300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
3310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
332ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        mMhlDevices = Collections.emptyList();
333f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
334f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        initPortInfo();
33575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
3368692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
33763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
338f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mCecController != null) {
3390608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register broadcast receiver for power state change.
34038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
34138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
34238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
3431ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
3441ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
3450608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
3460608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register ContentObserver to monitor the settings change.
3470608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            registerContentObserver();
34838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
3497ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
35038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
3517fa3a66470d2133796defd14a0600578758882acJinsuk Kim    @Override
3527fa3a66470d2133796defd14a0600578758882acJinsuk Kim    public void onBootPhase(int phase) {
3537fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
3547fa3a66470d2133796defd14a0600578758882acJinsuk Kim            mTvInputManager = (TvInputManager) getContext().getSystemService(
3557fa3a66470d2133796defd14a0600578758882acJinsuk Kim                    Context.TV_INPUT_SERVICE);
3567fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
3577fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
3587fa3a66470d2133796defd14a0600578758882acJinsuk Kim
3597fa3a66470d2133796defd14a0600578758882acJinsuk Kim    TvInputManager getTvInputManager() {
3607fa3a66470d2133796defd14a0600578758882acJinsuk Kim        return mTvInputManager;
3617fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
3627fa3a66470d2133796defd14a0600578758882acJinsuk Kim
3637fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void registerTvInputCallback(TvInputCallback callback) {
3647fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (mTvInputManager == null) return;
3657fa3a66470d2133796defd14a0600578758882acJinsuk Kim        mTvInputManager.registerCallback(callback, mHandler);
3667fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
3677fa3a66470d2133796defd14a0600578758882acJinsuk Kim
3687fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void unregisterTvInputCallback(TvInputCallback callback) {
3697fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (mTvInputManager == null) return;
3707fa3a66470d2133796defd14a0600578758882acJinsuk Kim        mTvInputManager.unregisterCallback(callback);
3717fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
3727fa3a66470d2133796defd14a0600578758882acJinsuk Kim
37325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
37425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
37525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
3760608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    private void onInitializeCecComplete(int initiatedBy) {
377fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
378fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
379fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
380fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
381fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
38225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
3835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
3840608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
3850608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        int reason = -1;
3860608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        switch (initiatedBy) {
3870608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_BOOT_UP:
3880608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START;
3890608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3900608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_ENABLE_CEC:
3910608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING;
3920608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3930608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_SCREEN_ON:
3940608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_WAKE_UP_MESSAGE:
3950608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
3960608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3970608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
3980608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (reason != -1) {
3990608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            invokeVendorCommandListenersOnControlStateChanged(true, reason);
40025c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
40125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
40225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
4035008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
4045008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
4055008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
4065008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
4075008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
4085008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
4095008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
4105008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
4115008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
4125691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        for (String s : settings) {
4135008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
4145008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
4155008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
4165008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4175008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4185008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
4195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
4205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
4215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
4225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
423f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        // onChange is set up to run in service thread.
4245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
4255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
4265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
4275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
4285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
4295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
4305008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
4315008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4325008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
4335008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoWakeup(enabled);
434350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                    setCecOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
4355008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
4375008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoDeviceOff(enabled);
4385008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
4395008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4405008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
44108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
4425008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4435008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
444f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
4455008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4465008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
4475008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
4485008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4495008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
4515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
4525008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4535008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4547ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
4557ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
4565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
4577ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
4587ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
4597ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
4607ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
4615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
4625008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
464fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
4655008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
466b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        initializeLocalDevices(initiatedBy);
467a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
468a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
469a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
470b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void initializeLocalDevices(final int initiatedBy) {
471a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
472b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        // A container for [Device type, Local device info].
473b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
474b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (int type : mLocalDevices) {
4756f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
4766f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            if (localDevice == null) {
4776f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim                localDevice = HdmiCecLocalDevice.create(this, type);
4786f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            }
4793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
480b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            localDevices.add(localDevice);
481b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
4826f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // It's now safe to flush existing local devices from mCecController since they were
4836f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // already moved to 'localDevices'.
4846f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        clearLocalDevices();
485b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        allocateLogicalAddress(localDevices, initiatedBy);
486b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    }
487b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
488b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    @ServiceThreadOnly
489b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
490b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            final int initiatedBy) {
491b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        assertRunOnServiceThread();
49289ec14e48f4a1bdf291cda9fba7b8172f55a2447Yuncheol Heo        mCecController.clearLogicalAddress();
493b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
494b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final int[] finished = new int[1];
495b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
496b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            mCecController.allocateLogicalAddress(localDevice.getType(),
4973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
4983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
4993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
500c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
5013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
5023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
503410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
504410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
505410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
506410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
5073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
5083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
5093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
510b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        allocatedDevices.add(localDevice);
5113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
5123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5134893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
514b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                    if (allocatingDevices.size() == ++finished[0]) {
515b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        if (initiatedBy != INITIATED_BY_HOTPLUG) {
516b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // In case of the hotplug we don't call onInitializeCecComplete()
517b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // since we reallocate the logical address only.
5180608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                            onInitializeCecComplete(initiatedBy);
519b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        }
520b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        notifyAddressAllocated(allocatedDevices, initiatedBy);
5213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
5223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
5233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
5243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
5253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
527a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
528b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
529a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
530b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (HdmiCecLocalDevice device : devices) {
531b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            int address = device.getDeviceInfo().getLogicalAddress();
532fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
5333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
5343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
5370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
538a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5392b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
540a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
5420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
5430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
5440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
5450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
5460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
5470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
5480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
5492b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
5502b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
5512b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
55230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
55330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
554e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
5552b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
55630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
55730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
558e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
5590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
56030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
56130c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
562e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
5630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
564f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
565f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
566f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : mhlPortInfo) {
567f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (info.isMhlSupported()) {
568f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                mhlSupportedPorts.add(info.getId());
5690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
570f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
5710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
572f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
573f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // cec port info if we do not have have port that supports MHL.
574f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mhlSupportedPorts.isEmpty()) {
575f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
576f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            return;
577f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
578f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
579f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
580f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (mhlSupportedPorts.contains(info.getId())) {
581f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
582f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        info.isCecSupported(), true, info.isArcSupported()));
583f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            } else {
584f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(info);
5852b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
5862b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
587f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mPortInfo = Collections.unmodifiableList(result);
5880340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5890340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
5902738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    List<HdmiPortInfo> getPortInfo() {
5912738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang        return mPortInfo;
5922738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    }
5932738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang
5940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
5950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
5960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
5970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
5980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
5990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
6000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
6012b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
6020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
6030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
604e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
605401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
606401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
607401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
608401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
609401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
610401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
611401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
612c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
613401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
614401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
615401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
616401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
617401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
618401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
619401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
620401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
621401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
622401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
623401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
624c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
6252b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
626401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
627401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
62809ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
6292b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
63009ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
63109ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
632401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
633e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
634e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
635e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
636e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
637e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
638e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
639e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
640e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
641e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
642e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
643e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
644e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
645e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
646e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
647e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
64867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
649e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
650c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
651c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
6523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
6533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
6543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
6553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
6563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
6583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
6593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
6603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
6613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
6623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
6633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
665a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
66661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
6670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
66879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
66979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
67079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
67179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
6728960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        return tv.getCecDeviceInfo(logicalAddress);
673a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
674a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
6753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
676092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
677092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
678092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
679092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
680092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
681092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
682092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
68360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
68460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
68560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
686339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        int portId = pathToPortId(physicalAddress);
6872b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
6882b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
68960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
69060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
69160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
69260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
69379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
69467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
69567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
69667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
69763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
69863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
69963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
70063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
70163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
70263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
70363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
70463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
70563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
70663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
70767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
708c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
709c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
710c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
711d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
712c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
713a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
714d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
715a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7164c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
7175f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
7185f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
7192e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.error("Invalid message type:" + command);
7205f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
7215f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
7225f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
7235f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
724d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
725d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
726a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
727d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
728a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7295f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
730c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
731c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
7326aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
7336aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
7346aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
7356aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
7366aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
7376aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
7386aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
7396aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
7406aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
7416aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
7426aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
7436aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
744a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
745a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
746a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7474c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        int errorCode = mMessageValidator.isValid(message);
7484c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (errorCode != HdmiCecMessageValidator.OK) {
749a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // We'll not response on the messages with the invalid source or destination
750a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // or with parameter length shorter than specified in the standard.
7514c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
7524c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo                maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
7534c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            }
7544c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            return true;
75575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
756092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
757092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
758092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
75979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
76079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
76160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
76260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
763a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
764092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
765a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
766092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
76779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
768c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
769092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
770092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
771092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
77260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
773c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
7742e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("Unhandled cec command:" + message);
7753a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
776092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
777a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
778a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
77967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
78067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
78167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
782ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim     * @param portId hdmi port number where hot plug event issued.
78367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
78467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
785a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
786ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void onHotplug(int portId, boolean connected) {
78760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
788b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
789b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        if (connected && !isTvDevice()) {
790b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
791b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            for (int type : mLocalDevices) {
792b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
793b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                if (localDevice == null) {
794b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice = HdmiCecLocalDevice.create(this, type);
795b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice.init();
796b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                }
797b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                localDevices.add(localDevice);
798b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            }
799b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
800b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
801b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
80279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
803ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            device.onHotplug(portId, connected);
80460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
805ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
80667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
80767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
80802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
80902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
81002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
81102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
81202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
8131de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
8140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
81502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
8160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
81702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
818a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
8191de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
8201de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
821a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
8221de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
8231de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
8240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
8250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
8260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
827c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
8280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
8290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
8300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
831c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
8320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
8330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
8340f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
8350f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
83602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
83702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
83860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
83960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
84060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
84160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
84260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
84379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
84479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
84579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
84679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
84779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
848b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
849b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
850b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
851b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
852b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
853b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
854b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
855b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
856b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
857b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
858b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
859b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
860b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
8611a6be6ed3962735f12dbd5ce1bca758120c8fb8dJungshik Jang                    AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
862b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
8633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
8643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
865ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
866f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
867f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (SystemAudioModeChangeListenerRecord record :
868f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mSystemAudioModeChangeListenerRecords) {
869f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeSystemAudioModeChangeLocked(record.mListener, enabled);
870f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
871ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
872ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
873ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
874410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
87542c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
87642c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
87761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
8782b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
8792b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
8803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
8813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
8827df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8837df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
8847df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
88593eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim        // Hotplug event is used to add/remove MHL devices as TV input.
8867df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
8873b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub newDevice = new HdmiMhlLocalDeviceStub(this, portId);
8883b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub oldDevice = mMhlController.addLocalDevice(newDevice);
8897df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
8907df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
8917df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
8927df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
89393eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            invokeDeviceEventListeners(newDevice.getInfo(), DEVICE_EVENT_ADD_DEVICE);
89493eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            updateSafeMhlInput();
8957df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8963b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = mMhlController.removeLocalDevice(portId);
8977df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
8987df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
89993eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
90093eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                updateSafeMhlInput();
9017df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
9027df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
9037df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
9047df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
905ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
9067df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9077df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
9087df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
909a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusModeChanged(int portId, int busmode) {
9107df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
9113b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
9127df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
913a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setBusMode(busmode);
9147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
915a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus mode change[portId:" + portId +
916a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim                    ", busmode:" + busmode + "]");
9177df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
9187df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9197df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
9207df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
921a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusOvercurrent(int portId, boolean on) {
9227df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
9233b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
9247df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
925a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.onBusOvercurrentDetected(on);
9267df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
927a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus overcurrent event[portId:" + portId + "]");
9287df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
9297df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9307df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
9317df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
932a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlDeviceStatusChanged(int portId, int adopterId, int deviceId) {
9337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
9343b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
935ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
9367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
937a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setDeviceStatusChange(adopterId, deviceId);
9387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
939a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for device status event[portId:"
9407df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
9417df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
9427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9437df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
944ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @ServiceThreadOnly
945ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private void updateSafeMhlInput() {
946ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        assertRunOnServiceThread();
947ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<HdmiDeviceInfo> inputs = Collections.emptyList();
9483b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        SparseArray<HdmiMhlLocalDeviceStub> devices = mMhlController.getAllLocalDevices();
949ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        for (int i = 0; i < devices.size(); ++i) {
9503b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = devices.valueAt(i);
951ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiDeviceInfo info = device.getInfo();
952ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            if (info != null) {
953ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                if (inputs.isEmpty()) {
954ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                    inputs = new ArrayList<>();
955ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                }
956ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                inputs.add(device.getInfo());
957ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            }
958ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
959ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        synchronized (mLock) {
960ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            mMhlDevices = inputs;
961ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
962ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
963ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
964ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> getMhlDevicesLocked() {
965ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return mMhlDevices;
966ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
967ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
968b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private class HdmiMhlVendorCommandListenerRecord implements IBinder.DeathRecipient {
969b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        private final IHdmiMhlVendorCommandListener mListener;
970f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
971b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener) {
972f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mListener = listener;
973f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
974f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
975f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        @Override
976f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        public void binderDied() {
977b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.remove(this);
978f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
979f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
980f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
98178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
98278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
98378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
98478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
98578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
98678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
98778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
98878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
98978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
99078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
99178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
99278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
99378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
99478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
99578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
99678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
99778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
9996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
10006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
10016d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
10026d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
10036d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
10046d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
10056d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
1006ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
10076d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
10086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
10096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
10106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
10116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
10126d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1013ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
101438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
1015ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1016ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
1017ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
1018ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1019ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1020ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1021ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
1022ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
1023ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
1024ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1025ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1026ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1027ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1028119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
1029119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
1030119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
1031119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1032119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
1033119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
1034119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
1035119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1036119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1037119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1038119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
1039119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
1040119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
1041119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1042119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1043119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1044119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
104512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
1046f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiRecordListener mListener;
1047f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1048f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public HdmiRecordListenerRecord(IHdmiRecordListener listener) {
1049f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1050f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1051f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1052b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1053b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
1054b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
1055f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mRecordListenerRecord = null;
1056b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1057b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1058b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1059b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
106078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
106178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
106278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
106378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
106478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
106578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
106678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
106778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
10690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
10700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
10710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
107278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
10730340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
107478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
107578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
107678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
107761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
1078b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
10797e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
10807e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
10817e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
10827e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
10837e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10847e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
10857e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
108661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
108761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
108861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
10897e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10907e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
109161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
109261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
10937e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10947e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
10957e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
10967e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
10977e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
10988960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
1099a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
1100a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
1101a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
1102a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
110372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
110472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
110572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
110672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
110779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1108a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
1109a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1110c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1111a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
1112a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
11133b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
1114f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1115f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        if (device.getPortId() == tv.getActivePortId()) {
1116f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
111787f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            return;
111887f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        }
1119f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // Upon selecting MHL device, we send RAP[Content On] to wake up
1120f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // the connected mobile device, start routing control to switch ports.
1121f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // callback is handled by MHL action.
1122f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.turnOn(callback);
11237c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                        tv.doManualPortSwitching(device.getPortId(), null);
1124f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
112587f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    }
11268960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                    tv.deviceSelect(deviceId, callback);
1127a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
1128a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
1129a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
1130a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
1131a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
1132a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
1133a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1134a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1135a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1136a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
113772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
113872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
113972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
114072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
1141a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
1142a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
1143a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1144c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1145a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1146a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
11478333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
1148a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1149a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1150a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1151a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1152a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
1153c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
1154a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1155a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1156a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1157a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
11583b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(mActivePortId);
1159f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1160f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.sendKeyEvent(keyCode, isPressed);
1161f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
11624612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    }
11634612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mCecController != null) {
11644612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
11654612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (localDevice == null) {
11664612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            Slog.w(TAG, "Local device not available");
11674612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
11684612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
11694612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        localDevice.sendKeyEvent(keyCode, isPressed);
1170a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1171a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1172a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1173a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1174a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1175a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
11767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
117778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
11827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
118478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
118578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
118678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
118878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
11937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
119578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
119678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
119778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
119978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1200f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addHotplugEventListener(listener);
120178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
120278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
120378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
12047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
120578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1206f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.removeHotplugEventListener(listener);
120778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
12086d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
12096d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
12106d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
12116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
1212f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addDeviceEventListener(listener);
12136d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
12146d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
12156d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
12166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
12176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
12182738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang            return HdmiControlService.this.getPortInfo();
12196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1220ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1221ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1222ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1223ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1224ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1225ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1226ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1227ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1228e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1229ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1230ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1231ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1232ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1233ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1234ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1235ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1236ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1237ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1238377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1239ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1240ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1241ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1242ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1243ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1244ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1245ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1246ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1247ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1248ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1249ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1250c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1251ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1252ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1253ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1254ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1255ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1256ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1257ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1258ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1259ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1260ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1261ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1262ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1263ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1264ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1265ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1266ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1267ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1268ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1269ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1270ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
127192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
127292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
12739c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
12749c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12759c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
12769c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12779c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12789c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
127961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
12809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
12829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
12839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1284ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            synchronized (mLock) {
1285ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                List<HdmiDeviceInfo> cecDevices = (tv == null)
1286ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1287ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        : tv.getSafeExternalInputsLocked();
1288ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
12899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12909c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1292bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // Returns all the CEC devices on the bus including system audio, switch,
1293bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // even those of reserved type.
1294bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        @Override
1295bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        public List<HdmiDeviceInfo> getDeviceList() {
1296bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            enforceAccessPermission();
1297bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1298bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            synchronized (mLock) {
1299bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                return (tv == null)
1300bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1301bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        : tv.getSafeCecDevicesLocked();
1302bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            }
1303bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        }
1304bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim
13059c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
130641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
130741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
130841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
130941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
131041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
131141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
131241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
131341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
131441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
131541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
131641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
131741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
131841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
131941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
132041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
132141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
132241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
132341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
132441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
132541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
132641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
132741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
132841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
132941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
133041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
133141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
133241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
133341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
133441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
133541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
133641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
133741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
133841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1339a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1340a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1341a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1342a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1343a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1344a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1345a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
134638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1347a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1348a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1349a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1350a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1351a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1352160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1353160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
13544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
13554d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
13564d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
13574d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
13584d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
13594d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
13604d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1361119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1362119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1363119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1364119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1365119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1366f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1367119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1368119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1369119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1370119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1371119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1372119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1373119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1374119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1375119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1376119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1377119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1378119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1379119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1380119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1381119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1382119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1383119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1384119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1385119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1386119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1387119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1388119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1389119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1390119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
139112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1392a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1393a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1394d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        public void sendStandby(final int deviceType, final int deviceId) {
1395d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            enforceAccessPermission();
1396d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            runOnServiceThread(new Runnable() {
1397d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                @Override
1398d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                public void run() {
1399d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1400d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    if (device == null) {
1401d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        Slog.w(TAG, "Local device not available");
1402d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        return;
1403d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    }
1404d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    device.sendStandby(deviceId);
1405d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                }
1406d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            });
1407d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        }
1408d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim
1409d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        @Override
141012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
1411b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
141212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1413b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1414b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1415b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1416b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1417b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1418b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1419b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1420b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1421b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1422b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1423b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1424b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1425b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1426b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1427b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1428b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1429b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1430b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1431b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1432b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1433b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1434b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1435b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1436b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1437b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1438b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1439b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1440b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1441b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1442b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1443a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1444a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1445a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1446b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1447b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1448b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1449b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1450b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1451b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1452b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1453b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1454b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1455b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1456b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1457b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1458b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1459bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1460bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1461bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1462b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1463b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1464b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1465b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1466b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1467b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1468b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1469b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1470b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1471b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1472b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1473b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1474b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1475a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1476f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1477f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1478b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void sendMhlVendorCommand(final int portId, final int offset, final int length,
1479f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                final byte[] data) {
1480f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1481f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            runOnServiceThread(new Runnable() {
1482f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                @Override
1483f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                public void run() {
1484f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (!isControlEnabled()) {
1485f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Hdmi control is disabled.");
1486f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return ;
1487f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
14883b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
1489f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (device == null) {
1490f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Invalid port id:" + portId);
1491f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return;
1492f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
1493b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    mMhlController.sendVendorCommand(portId, offset, length, data);
1494f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1495f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            });
1496f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1497f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1498f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1499b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void addHdmiMhlVendorCommandListener(
1500b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                IHdmiMhlVendorCommandListener listener) {
1501f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1502b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            HdmiControlService.this.addHdmiMhlVendorCommandListener(listener);
1503f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1504959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1505959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        @Override
1506959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1507959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1508959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1509959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1510959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
1511959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mProhibitMode: " + mProhibitMode);
1512959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            if (mCecController != null) {
1513959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("mCecController: ");
1514959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.increaseIndent();
1515959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                mCecController.dump(pw);
1516959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.decreaseIndent();
1517959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1518959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPortInfo: ");
1519959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.increaseIndent();
1520959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
1521959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("- " + hdmiPortInfo);
1522959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1523959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.decreaseIndent();
1524959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPowerStatus: " + mPowerStatus);
1525959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
152678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
152778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1528a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
152979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
153079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
153179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
15327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
15337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1534c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
15357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
15367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
153779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
153878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
153978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1540a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
154179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
154279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
154379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
15447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
15457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1546c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
15477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
15487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
154979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
155078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
155178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
155278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
155378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
155478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
155578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
155678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
155778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
155878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
155978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
156078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
156178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
156278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
156378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
156478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
156578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
156678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
156778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
156878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
156978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
157078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
157178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
157278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
157378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
157478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
157578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
15767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
15776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
15784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
15794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
15804893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
15814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
15824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
15834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
15844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
15856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
15864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
15874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
15884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
15894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
159061daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
15914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
1592f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
15934893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
1594f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onStatusChanged(device, status);
15954893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
15964893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
15976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
15986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
15996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
16006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
16016d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1602ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1603ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1604ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1605ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1606ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1607ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1608ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1609ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1610ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1611ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1612ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1613ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1614ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1615ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1616ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1617ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1618ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1619ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1620ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1621ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1622ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1623ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1624ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1625ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1626ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1627ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1628ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
16299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
1630f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiInputChangeListener mListener;
1631f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1632f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public InputChangeListenerRecord(IHdmiInputChangeListener listener) {
1633f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1634f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1635f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
16369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
16379c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
16389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
1639f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mInputChangeListenerRecord = null;
16409c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16419c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16429c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
16449c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
16459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1646f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mInputChangeListenerRecord = new InputChangeListenerRecord(listener);
16479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
16489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
16499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
16509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
16519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
16529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
165661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
16579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1658f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mInputChangeListenerRecord != null) {
16599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
1660f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mInputChangeListenerRecord.mListener.onChanged(info);
16619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
16629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
16639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
16649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
166812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1669b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1670f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord(listener);
1671b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
167212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1673b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
167412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1675b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1676b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1677b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1678b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1679b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1680b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1681f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
168212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1683f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress);
168412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
168512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1686b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1687b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1688b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1689b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1690b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1691b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1692326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeOneTouchRecordResult(int recorderAddress, int result) {
169312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1694f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
169512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1696326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onOneTouchRecordResult(recorderAddress, result);
169712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
169812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
169912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
170012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
170112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
170212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
170312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
1704326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeTimerRecordingResult(int recorderAddress, int result) {
170512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1706f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
170712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1708326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onTimerRecordingResult(recorderAddress, result);
170912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1710e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1711e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1712e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1713e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1714e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1715e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1716326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeClearTimerRecordingResult(int recorderAddress, int result) {
1717e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1718f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
1719e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1720326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onClearTimerRecordingResult(recorderAddress,
1721326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                            result);
1722e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1723e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
172412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
172512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
172612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
172712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
172812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
17297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
17307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
17317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
17327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
17337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
17347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
17357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
173663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1737f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener,
1738ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1739ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1740ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1741ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1742ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1743ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1744ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1745ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
17464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
17474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
174860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
1749f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
1750f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeHotplugEventListenerLocked(record.mListener, event);
175160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
175260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
175360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
175460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
17554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
175660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
175760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
175860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
175960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
176060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
176160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1762e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1763e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
176479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
176561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
176679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
176779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1768e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1769b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
1770e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1771e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
177279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1773c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
177461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
177560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1776a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1777a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1778a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1779a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
178092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
178192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
178292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
178392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
178492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
178592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
178638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1787f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
178838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
1789f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
179038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
179138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
179238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1793f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
179438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1795f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1796c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1797c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
179838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
179938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1800f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
180138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1802f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1803c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1804c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
180538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
180638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1807f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
180838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1809f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1810c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
181138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
181238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
181338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
181438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
181538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1816fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
181738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
181838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
181938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
182038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
182138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
182238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
182338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
182438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
182538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
182638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
182738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1828c12035cd40d01b032013f515cb509e6c8791cf65Jeff Brown        pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
182938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
183038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
183138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
183238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
183338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
183438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
183538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1836c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
183738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1838a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1839fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1840fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1841fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1842fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1843fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1844a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
184538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
184638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
184738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
184838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
184938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
185038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
185138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
185238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
185338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1854c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
18550608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        invokeVendorCommandListenersOnControlStateChanged(false,
18560608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
18574fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18584fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
18594fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
18604fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
18614fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
18624fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
18634fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
18644fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
18654fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
18664b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
18674b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
18684b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
18694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
18704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
18714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
18724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
18734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18741ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
18751ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private void onLanguageChanged(String language) {
18761ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        assertRunOnServiceThread();
18771ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        mLanguage = language;
18781ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
18791ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        if (isTvDevice()) {
18801ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            tv().broadcastMenuLanguage(language);
18811ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        }
18821ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    }
18831ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
1884f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
1885f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    String getLanguage() {
1886f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1887f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        return mLanguage;
1888f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    }
1889f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang
18904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
1891350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang        if (mCecController != null) {
1892350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
1893350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                device.disableDevice(mStandbyMessageReceived, callback);
1894350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            }
189538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1896350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang
1897f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.clearAllLocalDevices();
189838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
189938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
190038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
19014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
19024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
19034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
19044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
19054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
19064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
19074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
19084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
19094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
19114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
191238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
19134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
19144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1915c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
191638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
191738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1918c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
191938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
19204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
192138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
192238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
19235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
192438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
19254d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1926119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1927119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1928119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1929119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1930119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1931119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1932119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1933119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1934119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1935119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1936119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1937119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1938119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
19390608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress,
19400608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            byte[] params, boolean hasVendorId) {
1941119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1942d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            if (mVendorCommandListenerRecords.isEmpty()) {
1943d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                return false;
1944d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            }
1945119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1946119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1947119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1948119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1949119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
19500608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
1951119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1952119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1953119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1954119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1955d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            return true;
1956119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1957119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1958119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
19590608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) {
19600608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        synchronized (mLock) {
19610608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            if (mVendorCommandListenerRecords.isEmpty()) {
19620608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                return false;
19630608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
19640608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
19650608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                try {
19660608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onControlStateChanged(enabled, reason);
19670608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                } catch (RemoteException e) {
19680608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e);
19690608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                }
19700608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
19710608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            return true;
19720608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
19730608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    }
19740608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
1975b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
1976b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        HdmiMhlVendorCommandListenerRecord record =
1977b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                new HdmiMhlVendorCommandListenerRecord(listener);
1978f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        try {
1979f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            listener.asBinder().linkToDeath(record, 0);
1980f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        } catch (RemoteException e) {
1981f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            Slog.w(TAG, "Listener already died.");
1982f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            return;
1983f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1984f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1985f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1986b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.add(record);
1987f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1988f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
1989f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1990b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    void invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data) {
1991f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1992b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            for (HdmiMhlVendorCommandListenerRecord record : mMhlVendorCommandListenerRecords) {
1993f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                try {
1994f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onReceived(portId, offest, length, data);
1995f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                } catch (RemoteException e) {
1996b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    Slog.e(TAG, "Failed to notify MHL vendor command", e);
1997f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1998f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
1999f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
2000f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
2001f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
20024d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
20034d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
20044d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
20054d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
20064d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
20074d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
20084d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
20094d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
20104d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
20114d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
20124d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
20134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
2015350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang    void setCecOption(int key, int value) {
20165008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
20175008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
20185008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
20195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
20205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
20215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
20224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
20234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20240608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (!enabled) {
20250608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Call the vendor handler before the service is disabled.
20260608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            invokeVendorCommandListenersOnControlStateChanged(false,
20270608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
20280608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
20295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        int value = toInt(enabled);
20305008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_ENABLE, value);
2031f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.setOption(OPTION_MHL_ENABLE, value);
20324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
20344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
20354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
20364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
2038fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
20394fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
20404fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
20414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
20424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
20434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
20444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
20454fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
20464fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
20474fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
20484fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
2049867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
2050867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
2051867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
2052867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
2053867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
2054e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2055e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // Resets last input for MHL, which stays valid only after the MHL device was selected,
2056e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // and no further switching is done.
2057e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        setLastInputForMhl(Constants.INVALID_PORT_ID);
2058e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2059e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2060e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2061e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void setLastInputForMhl(int portId) {
2062e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2063e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mLastInputMhl = portId;
2064e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2065e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2066e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2067e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    int getLastInputForMhl() {
2068e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2069e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        return mLastInputMhl;
2070e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2071e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2072e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    /**
2073e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * Performs input change, routing control for MHL device.
2074e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     *
2075e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param portId MHL port, or the last port to go back to if {@code contentOn} is false
2076e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param contentOn {@code true} if RAP data is content on; otherwise false
2077e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     */
2078e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2079e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void changeInputForMhl(int portId, boolean contentOn) {
2080e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2081e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
2082e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
2083e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            @Override
2084e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            public void onComplete(int result) throws RemoteException {
2085e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // Keep the last input to switch back later when RAP[ContentOff] is received.
2086e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // This effectively sets the port to invalid one if the switching is for
2087e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // RAP[ContentOff].
2088e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                setLastInputForMhl(lastInput);
2089e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            }
2090e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        });
2091e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2092e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // MHL device is always directly connected to the port. Update the active port ID to avoid
2093e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // unnecessary post-routing control task.
2094e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().setActivePortId(portId);
2095e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2096e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // The port is either the MHL-enabled port where the mobile device is connected, or
2097f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // the last port to go back to when turnoff command is received. Note that the last port
2098e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // may not be the MHL-enabled one. In this case the device info to be passed to
2099e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // input change listener should be the one describing the corresponding HDMI port.
21003b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
210193eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim        HdmiDeviceInfo info = (device != null) ? device.getInfo() : mPortDeviceMap.get(portId);
2102e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        invokeInputChangeListener(info);
2103867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
210408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
2105e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim   void setMhlInputChangeEnabled(boolean enabled) {
2106f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim       mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
210708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
210808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
210908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
211008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
211108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
211208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
211308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
211408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
211508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
211608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
211708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
2118339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang
2119339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    @ServiceThreadOnly
2120339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    void displayOsd(int messageId) {
2121339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        assertRunOnServiceThread();
2122339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
2123339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
2124339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
2125339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                HdmiControlService.PERMISSION);
2126339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    }
21272e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang
21282e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    @ServiceThreadOnly
21292e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    void displayOsd(int messageId, int extra) {
21302e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        assertRunOnServiceThread();
21312e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
21322e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
21332b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
21342e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
21352e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiControlService.PERMISSION);
21362e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    }
21370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
2138