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_MHL_ENABLE;
245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
265b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_SERVICE_CONTROL;
275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
2938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
307ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
3238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
3338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.database.ContentObserver;
35c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
367d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo;
3760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
39d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
40d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
416d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
42d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
44b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kimimport android.hardware.hdmi.IHdmiMhlVendorCommandListener;
4512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
46ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
47119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
48bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Choimport android.hardware.tv.cec.V1_0.OptionKey;
49bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Choimport android.hardware.tv.cec.V1_0.SendMessageResult;
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;
704893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
71fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils;
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;
78959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.FileDescriptor;
79959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.PrintWriter;
8078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
81f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
841ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heoimport java.util.Locale;
85bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Choimport libcore.util.EmptyArray;
86a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
935fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim    private final Locale HONG_KONG = new Locale("zh", "HK");
945fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim    private final Locale MACAU = new Locale("zh", "MO");
950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
96c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
9778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
98fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
99fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
100fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
101fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
102fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
103b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    static final int INITIATED_BY_HOTPLUG = 4;
104fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
105e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    // The reason code representing the intent action that drives the standby
106e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    // procedure. The procedure starts either by Intent.ACTION_SCREEN_OFF or
107e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    // Intent.ACTION_SHUTDOWN.
108e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    static final int STANDBY_SCREEN_OFF = 0;
109e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    static final int STANDBY_SHUTDOWN = 1;
110e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim
111d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
112d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
113d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
114d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
115d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
116d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
117d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
118ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
120bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho         * <li>{@link SendMessageResult#SUCCESS}
121bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho         * <li>{@link SendMessageResult#NACK}
122bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho         * <li>{@link SendMessageResult#BUSY}
123bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho         * <li>{@link SendMessageResult#FAIL}
1244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
125d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
126d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
127d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
128d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
12902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
13002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
13102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
13202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
13302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
13402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
13502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
13602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
13702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
13802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
13902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
14002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
1411ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
142f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        @ServiceThreadOnly
14338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
14438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
145f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang            assertRunOnServiceThread();
14638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
14738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
14838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
149e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                        onStandby(STANDBY_SCREEN_OFF);
15038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
15138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
15238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
15338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
15438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
15538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
15638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
1571ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                case Intent.ACTION_CONFIGURATION_CHANGED:
1585fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                    String language = getMenuLanguage();
1591ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    if (!mLanguage.equals(language)) {
1601ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                        onLanguageChanged(language);
1611ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    }
1621ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    break;
163e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                case Intent.ACTION_SHUTDOWN:
164e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                    if (isPowerOnOrTransient()) {
165e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                        onStandby(STANDBY_SHUTDOWN);
166e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                    }
167e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                    break;
16838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
16938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1705fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim
1715fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        private String getMenuLanguage() {
1725fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            Locale locale = Locale.getDefault();
1735fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
1745fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Android always returns "zho" for all Chinese variants.
1755fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Use "bibliographic" code defined in CEC639-2 for traditional
1765fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Chinese used in Taiwan/Hong Kong/Macau.
1775fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return "chi";
1785fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            } else {
1795fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return locale.getISO3Language();
1805fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            }
1815fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        }
18238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
18338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1840792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
18978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
19078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
19178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
19478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
19578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1964893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
19778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
19878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
19978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
200f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for device event listener to handle the caller killed in action.
2014893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
2026d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
2036d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
2046d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
205f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for vendor command listener to handle the caller killed in action.
206119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
207119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
208119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
209119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
2109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
2119c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
2129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
213b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
21412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
215b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
21692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
21792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
21892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
21992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
22092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
2214d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
2224d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
2234d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
2244d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
2254d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2264d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
227ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
228ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
229ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
230ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2314893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2355008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
236f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private final HdmiControlBroadcastReceiver
237f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
238f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2462b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
24730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2482b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2492b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
25030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2512b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
252e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Map from port ID to HdmiDeviceInfo.
253e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
254e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
25575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
25675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
25738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
258c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
25938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
26038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
2611ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private String mLanguage = Locale.getDefault().getISO3Language();
2621ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2631ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
26438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
26538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
266fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
267fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
268fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
269867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
270867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
271867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
272f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Set to true while the input change by MHL is allowed.
273f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
274f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private boolean mMhlInputChangeEnabled;
275f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
276b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    // List of records for MHL Vendor command listener to handle the caller killed in action.
277f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
278b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private final ArrayList<HdmiMhlVendorCommandListenerRecord>
279b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords = new ArrayList<>();
280f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
281f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
282f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private List<HdmiDeviceInfo> mMhlDevices;
283f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
284f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @Nullable
285781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim    private HdmiMhlControllerStub mMhlController;
286f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
2877fa3a66470d2133796defd14a0600578758882acJinsuk Kim    @Nullable
2887fa3a66470d2133796defd14a0600578758882acJinsuk Kim    private TvInputManager mTvInputManager;
2897fa3a66470d2133796defd14a0600578758882acJinsuk Kim
290e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    @Nullable
291e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    private PowerManager mPowerManager;
292e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim
293f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Last input port before switching to the MHL port. Should switch back to this port
294f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // when the mobile device sends the request one touch play with off.
295e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Gets invalidated if we go to other port/input.
296e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
297e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private int mLastInputMhl = Constants.INVALID_PORT_ID;
298e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
299964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim    // Set to true if the logical address allocation is completed.
300964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim    private boolean mAddressAllocated = false;
301964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
302964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim    // Buffer for processing the incoming cec messages while allocating logical addresses.
303964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim    private final class CecMessageBuffer {
304964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        private List<HdmiCecMessage> mBuffer = new ArrayList<>();
305964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
306964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        public void bufferMessage(HdmiCecMessage message) {
307964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            switch (message.getOpcode()) {
308964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                case Constants.MESSAGE_ACTIVE_SOURCE:
309964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    bufferActiveSource(message);
310964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    break;
311964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                case Constants.MESSAGE_IMAGE_VIEW_ON:
312964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                case Constants.MESSAGE_TEXT_VIEW_ON:
313964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    bufferImageOrTextViewOn(message);
314964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    break;
315964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    // Add here if new message that needs to buffer
316964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                default:
317964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    // Do not need to buffer messages other than above
318964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    break;
319964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
320964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
321964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
322964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        public void processMessages() {
323964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            for (final HdmiCecMessage message : mBuffer) {
324964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                runOnServiceThread(new Runnable() {
325964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    @Override
326964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    public void run() {
327964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                        handleCecCommand(message);
328964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    }
329964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                });
330964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
331964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            mBuffer.clear();
332964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
333964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
334964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        private void bufferActiveSource(HdmiCecMessage message) {
335964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) {
336964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                mBuffer.add(message);
337964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
338964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
339964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
340964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        private void bufferImageOrTextViewOn(HdmiCecMessage message) {
341964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) &&
342964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) {
343964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                mBuffer.add(message);
344964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
345964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
346964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
347964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        // Returns true if the message is replaced
348964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) {
349964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            for (int i = 0; i < mBuffer.size(); i++) {
350964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                HdmiCecMessage bufferedMessage = mBuffer.get(i);
351964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                if (bufferedMessage.getOpcode() == opcode) {
352964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    mBuffer.set(i, message);
353964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                    return true;
354964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                }
355964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            }
356964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            return false;
357964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
358964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim    }
359964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
360f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim    private final CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer();
361f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim
362f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim    private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer();
363964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
3640792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
3650792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
3667d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
3675008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
3680792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
3690792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
3707d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
3717d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
3727d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
3737d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
3747d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
3757d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
3767d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
3777d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
3787d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
3797d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
3807d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
3817d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
3827d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
3837d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
3840792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
3850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
3862f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
387c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
3887ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
3897ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
39008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
3918b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
392a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
3933ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
394a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
395fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
396a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
397a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
3980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
39908f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            return;
4000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
4010792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
402781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim        mMhlController = HdmiMhlControllerStub.create(this);
403f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (!mMhlController.isReady()) {
4040792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
4050792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
406ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        mMhlDevices = Collections.emptyList();
407f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
408f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        initPortInfo();
40975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
4108692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
41163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
412f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mCecController != null) {
4130608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register broadcast receiver for power state change.
41438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
41538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
41638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
4173059556f6092af0e292d4b3cff97ec772d2d6bd1Rob McConnell            filter.addAction(Intent.ACTION_SHUTDOWN);
4181ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
4191ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
4200608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
4210608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register ContentObserver to monitor the settings change.
4220608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            registerContentObserver();
42338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
4245b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim        mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
4257ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
42638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
4277fa3a66470d2133796defd14a0600578758882acJinsuk Kim    @Override
4287fa3a66470d2133796defd14a0600578758882acJinsuk Kim    public void onBootPhase(int phase) {
4297fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
4307fa3a66470d2133796defd14a0600578758882acJinsuk Kim            mTvInputManager = (TvInputManager) getContext().getSystemService(
4317fa3a66470d2133796defd14a0600578758882acJinsuk Kim                    Context.TV_INPUT_SERVICE);
432e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim            mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
4337fa3a66470d2133796defd14a0600578758882acJinsuk Kim        }
4347fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
4357fa3a66470d2133796defd14a0600578758882acJinsuk Kim
4367fa3a66470d2133796defd14a0600578758882acJinsuk Kim    TvInputManager getTvInputManager() {
4377fa3a66470d2133796defd14a0600578758882acJinsuk Kim        return mTvInputManager;
4387fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
4397fa3a66470d2133796defd14a0600578758882acJinsuk Kim
4407fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void registerTvInputCallback(TvInputCallback callback) {
4417fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (mTvInputManager == null) return;
4427fa3a66470d2133796defd14a0600578758882acJinsuk Kim        mTvInputManager.registerCallback(callback, mHandler);
4437fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
4447fa3a66470d2133796defd14a0600578758882acJinsuk Kim
4457fa3a66470d2133796defd14a0600578758882acJinsuk Kim    void unregisterTvInputCallback(TvInputCallback callback) {
4467fa3a66470d2133796defd14a0600578758882acJinsuk Kim        if (mTvInputManager == null) return;
4477fa3a66470d2133796defd14a0600578758882acJinsuk Kim        mTvInputManager.unregisterCallback(callback);
4487fa3a66470d2133796defd14a0600578758882acJinsuk Kim    }
4497fa3a66470d2133796defd14a0600578758882acJinsuk Kim
450e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    PowerManager getPowerManager() {
451e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        return mPowerManager;
452e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    }
453e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim
45425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
45525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
45625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
4570608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    private void onInitializeCecComplete(int initiatedBy) {
458fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
459fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
460fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
461fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
462fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
463de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim        if (isTvDeviceEnabled()) {
464bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho            mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
4650608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
4660608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        int reason = -1;
4670608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        switch (initiatedBy) {
4680608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_BOOT_UP:
4690608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START;
4700608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
4710608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_ENABLE_CEC:
4720608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING;
4730608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
4740608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_SCREEN_ON:
4750608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_WAKE_UP_MESSAGE:
4760608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
4770608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
4780608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
4790608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (reason != -1) {
4800608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            invokeVendorCommandListenersOnControlStateChanged(true, reason);
48125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
48225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
48325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
4845008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
4855008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
4865008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
4875008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
4885008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
4895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
490c1fa9afbcd1cafd205d46b2fd0bdaadccb7d29eaDonghyun Cho                Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
4915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
4925008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
4935008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
4945691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        for (String s : settings) {
4955008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
4965008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
4975008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
4985008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4995008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
5005008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
5015008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
5025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
5035008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
5045008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
505f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        // onChange is set up to run in service thread.
5065008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
5075008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
5085008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
5095008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
5105008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
5115008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
5125008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
5135008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
5145008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
515de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    if (isTvDeviceEnabled()) {
516de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                        tv().setAutoWakeup(enabled);
517de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    }
518bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho                    setCecOption(OptionKey.WAKEUP, enabled);
5195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
5205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
521e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                    for (int type : mLocalDevices) {
522e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
523dd371ec1a84f06a644e8c6f5179fd8bd5f11121bTerry Heo                        if (localDevice != null) {
524dd371ec1a84f06a644e8c6f5179fd8bd5f11121bTerry Heo                            localDevice.setAutoDeviceOff(enabled);
525dd371ec1a84f06a644e8c6f5179fd8bd5f11121bTerry Heo                        }
526de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    }
5275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
5285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
529c1fa9afbcd1cafd205d46b2fd0bdaadccb7d29eaDonghyun Cho                case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
530c1fa9afbcd1cafd205d46b2fd0bdaadccb7d29eaDonghyun Cho                    if (isTvDeviceEnabled()) {
531c1fa9afbcd1cafd205d46b2fd0bdaadccb7d29eaDonghyun Cho                        tv().setSystemAudioControlFeatureEnabled(enabled);
5322601f8da10db7f07fcca6a7184ccfbf6c79fbec1Donghyun Cho                    }
5332601f8da10db7f07fcca6a7184ccfbf6c79fbec1Donghyun Cho                    break;
5345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
53508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
5365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
5375008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
538f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
5395008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
5405008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
5415008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
5425008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
5435008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
5445008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
5455008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
5465008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
5475008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
5487ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
5497ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
5505008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
5517ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
5527ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
5537ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
5547ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
5555008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
5565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
5575008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
558fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
559964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        mAddressAllocated = false;
560bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
561bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho        mCecController.setLanguage(mLanguage);
562b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        initializeLocalDevices(initiatedBy);
563a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
564a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
565a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
566b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void initializeLocalDevices(final int initiatedBy) {
567a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
568b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        // A container for [Device type, Local device info].
569b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
570b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (int type : mLocalDevices) {
5716f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
5726f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            if (localDevice == null) {
5736f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim                localDevice = HdmiCecLocalDevice.create(this, type);
5746f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            }
5753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
576b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            localDevices.add(localDevice);
577b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
5786f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // It's now safe to flush existing local devices from mCecController since they were
5796f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // already moved to 'localDevices'.
5806f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        clearLocalDevices();
581b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        allocateLogicalAddress(localDevices, initiatedBy);
582b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    }
583b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
584b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    @ServiceThreadOnly
585b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
586b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            final int initiatedBy) {
587b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        assertRunOnServiceThread();
58889ec14e48f4a1bdf291cda9fba7b8172f55a2447Yuncheol Heo        mCecController.clearLogicalAddress();
589b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
590b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final int[] finished = new int[1];
591964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        mAddressAllocated = allocatingDevices.isEmpty();
592964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim
593f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        // For TV device, select request can be invoked while address allocation or device
594f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        // discovery is in progress. Initialize the request here at the start of allocation,
595f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        // and process the collected requests later when the allocation and device discovery
596f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        // is all completed.
597f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        mSelectRequestBuffer.clear();
598f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim
599b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
600b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            mCecController.allocateLogicalAddress(localDevice.getType(),
6013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
6023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
6033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
604c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
6053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
6063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
607410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
608410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
609410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
610410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
6113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
6123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
6133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
614b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        allocatedDevices.add(localDevice);
6153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
6163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
6174893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
618b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                    if (allocatingDevices.size() == ++finished[0]) {
619964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                        mAddressAllocated = true;
620b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        if (initiatedBy != INITIATED_BY_HOTPLUG) {
621b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // In case of the hotplug we don't call onInitializeCecComplete()
622b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // since we reallocate the logical address only.
6230608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                            onInitializeCecComplete(initiatedBy);
624b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        }
625b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        notifyAddressAllocated(allocatedDevices, initiatedBy);
626964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim                        mCecMessageBuffer.processMessages();
6273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
6283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
6293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
6303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
6313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
633a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
634b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
635a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
636b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (HdmiCecLocalDevice device : devices) {
637b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            int address = device.getDeviceInfo().getLogicalAddress();
638fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
6393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
640f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        if (isTvDeviceEnabled()) {
641f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim            tv().setSelectRequestBuffer(mSelectRequestBuffer);
642f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim        }
6433ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
645fc462b962cde0a96193fc780d6466cf8b0774112Donghyun Cho    boolean isAddressAllocated() {
646fc462b962cde0a96193fc780d6466cf8b0774112Donghyun Cho        return mAddressAllocated;
647fc462b962cde0a96193fc780d6466cf8b0774112Donghyun Cho    }
648fc462b962cde0a96193fc780d6466cf8b0774112Donghyun Cho
6490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
6500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
651a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
6522b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
653a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
6550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
6560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
6570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
6580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
6590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
6600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
6610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
6622b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
6632b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
6642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
66530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
66630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
667e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
6682b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
66930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
67030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
671e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
6720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
67330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
67430c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
675e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
6760340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
677f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
678f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
679f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : mhlPortInfo) {
680f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (info.isMhlSupported()) {
681f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                mhlSupportedPorts.add(info.getId());
6820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
683f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
6840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
685f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
686f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // cec port info if we do not have have port that supports MHL.
687f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mhlSupportedPorts.isEmpty()) {
688f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
689f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            return;
690f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
691f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
692f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
693f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (mhlSupportedPorts.contains(info.getId())) {
694f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
695f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        info.isCecSupported(), true, info.isArcSupported()));
696f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            } else {
697f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(info);
6982b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
6992b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
700f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mPortInfo = Collections.unmodifiableList(result);
7010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
7020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
7032738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    List<HdmiPortInfo> getPortInfo() {
7042738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang        return mPortInfo;
7052738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    }
7062738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang
7070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
7080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
7090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
7100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
7110340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
7120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
7130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
7142b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
7150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
7160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
717e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
718401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
719401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
720401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
721401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
722401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
723401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
724401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
725c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
726401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
727401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
728401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
729401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
730401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
731401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
732401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
733401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
734401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
735401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
736401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
737c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
7382b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
739401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
740401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
74109ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
7422b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
74309ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
74409ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
745401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
746e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
747e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
748e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
749e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
750e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
751e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
752e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
753e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
754e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
755e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
756e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
757e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
758e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
759e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
760e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
76167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
762e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
763c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
764c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
7653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
7663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
7673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
7683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
7693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
7713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
7723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
7733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
7743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
7753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
7763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
7773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
778a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
77961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
7800340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
781de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim        return tv() == null ? null : tv().getCecDeviceInfo(logicalAddress);
782a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
783a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
7846ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim    @ServiceThreadOnly
7856ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim    HdmiDeviceInfo getDeviceInfoByPort(int port) {
7866ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim        assertRunOnServiceThread();
7876ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim        HdmiMhlLocalDeviceStub info = mMhlController.getLocalDevice(port);
7886ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim        if (info != null) {
7896ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim            return info.getInfo();
7906ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim        }
7916ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim        return null;
7926ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim    }
7936ad7cbde22fe9752082ce01c9b9be213b07afefcJinsuk Kim
7943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
795092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
796092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
797092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
798092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
799092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
800092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
801092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
80260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
80360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
80460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
805339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        int portId = pathToPortId(physicalAddress);
8062b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
8072b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
80860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
80960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
81060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
81160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
8127b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim    @ServiceThreadOnly
8137b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim    boolean isConnected(int portId) {
8147b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim        assertRunOnServiceThread();
8157b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim        return mCecController.isConnected(portId);
8167b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim    }
8177b0cf6413218e5b5c549eea31733222fcffafabcJinsuk Kim
81879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
81967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
82067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
82167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
82263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
82363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
82463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
82563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
82663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
82763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
82863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
82963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
83063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
83163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
83267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
833c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
834c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
835c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
836d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
837c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
838a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
839d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
840a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
8414c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
8425f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
8435f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
8442e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.error("Invalid message type:" + command);
8455f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
846bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho                callback.onSendCompleted(SendMessageResult.FAIL);
8475f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
8485f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
849d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
850d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
851a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
852d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
853a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
8545f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
855c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
856c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
8576aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
8586aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
8596aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
8606aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
8616aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
8626aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
8636aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
8646aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
8656aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
8666aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
8676aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
8686aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
869a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
870a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
871a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
872964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        if (!mAddressAllocated) {
873964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            mCecMessageBuffer.bufferMessage(message);
874964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim            return true;
875964c00dd7b270dcf80aea3450bbfc23502965cceJinsuk Kim        }
8764c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        int errorCode = mMessageValidator.isValid(message);
8774c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (errorCode != HdmiCecMessageValidator.OK) {
878a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // We'll not response on the messages with the invalid source or destination
879a95f1a9b89ba321f39fd9926388d157f831db9b2Yuncheol Heo            // or with parameter length shorter than specified in the standard.
8804c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
8814c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo                maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
8824c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            }
8834c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            return true;
88475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
885092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
886092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
887092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
888bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho    void enableAudioReturnChannel(int portId, boolean enabled) {
889bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho        mCecController.enableAudioReturnChannel(portId, enabled);
89060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
89160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
892a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
893092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
894a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
895092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
89679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
897c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
898092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
899092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
900092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
90160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
902c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
9032e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("Unhandled cec command:" + message);
9043a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
905092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
906a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
907a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
90867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
90967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
91067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
911ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim     * @param portId hdmi port number where hot plug event issued.
91267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
91367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
914a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
915ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void onHotplug(int portId, boolean connected) {
91660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
917b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
918b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        if (connected && !isTvDevice()) {
919b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
920b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            for (int type : mLocalDevices) {
921b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
922b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                if (localDevice == null) {
923b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice = HdmiCecLocalDevice.create(this, type);
924b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice.init();
925b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                }
926b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                localDevices.add(localDevice);
927b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            }
928b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
929b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
930b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
93179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
932ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            device.onHotplug(portId, connected);
93360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
934ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
93567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
93667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
93702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
93802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
93902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
94002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
94102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
9421de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
9430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
94402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
9450f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
94602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
947a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
9481de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
9491de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
950a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
9511de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
9521de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
9530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
9540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
9550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
956c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
9570f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
9580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
9590f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
960c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
9610f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
9620f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
9630f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
9640f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
96502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
96602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
96760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
96860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
96960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
97060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
97160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
97279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
97379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
97479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
97579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
97679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
9776561845b560bdedfcc1ba6edfbdecd6122c02e00Donghyun Cho        if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated()) {
9786561845b560bdedfcc1ba6edfbdecd6122c02e00Donghyun Cho            return;
9796561845b560bdedfcc1ba6edfbdecd6122c02e00Donghyun Cho        }
980b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
981b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
982b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
983b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
984b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
985b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
986b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
987b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
988b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
989b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
990b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
991b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
992b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
9931a6be6ed3962735f12dbd5ce1bca758120c8fb8dJungshik Jang                    AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
994b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
9953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
9963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
997ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
998f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
999f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1000f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1001f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeSystemAudioModeChangeLocked(record.mListener, enabled);
1002f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
1003ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1004ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1005ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1006410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
100742c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
100842c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
100961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
10102b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
10112b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
10123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
10133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
10147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
10157df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
10167df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
101793eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim        // Hotplug event is used to add/remove MHL devices as TV input.
10187df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
10193b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub newDevice = new HdmiMhlLocalDeviceStub(this, portId);
10203b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub oldDevice = mMhlController.addLocalDevice(newDevice);
10217df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
10227df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
10237df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
10247df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
102593eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            invokeDeviceEventListeners(newDevice.getInfo(), DEVICE_EVENT_ADD_DEVICE);
102693eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            updateSafeMhlInput();
10277df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
10283b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = mMhlController.removeLocalDevice(portId);
10297df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
10307df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
103193eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
103293eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                updateSafeMhlInput();
10337df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
10347df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
10357df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
10367df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
1037ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
10387df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
10397df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
10407df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
1041a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusModeChanged(int portId, int busmode) {
10427df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
10433b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
10447df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
1045a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setBusMode(busmode);
10467df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
1047a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus mode change[portId:" + portId +
1048a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim                    ", busmode:" + busmode + "]");
10497df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
10507df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
10517df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
10527df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
1053a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusOvercurrent(int portId, boolean on) {
10547df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
10553b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
10567df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
1057a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.onBusOvercurrentDetected(on);
10587df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
1059a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus overcurrent event[portId:" + portId + "]");
10607df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
10617df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
10627df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
10637df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
1064a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlDeviceStatusChanged(int portId, int adopterId, int deviceId) {
10657df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
10663b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
1067ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
10687df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
1069a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setDeviceStatusChange(adopterId, deviceId);
10707df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
1071a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for device status event[portId:"
10727df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
10737df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
10747df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
10757df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
1076ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @ServiceThreadOnly
1077ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private void updateSafeMhlInput() {
1078ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        assertRunOnServiceThread();
1079ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<HdmiDeviceInfo> inputs = Collections.emptyList();
10803b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        SparseArray<HdmiMhlLocalDeviceStub> devices = mMhlController.getAllLocalDevices();
1081ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        for (int i = 0; i < devices.size(); ++i) {
10823b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = devices.valueAt(i);
1083ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiDeviceInfo info = device.getInfo();
1084ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            if (info != null) {
1085ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                if (inputs.isEmpty()) {
1086ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                    inputs = new ArrayList<>();
1087ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                }
1088ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                inputs.add(device.getInfo());
1089ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            }
1090ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
1091ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        synchronized (mLock) {
1092ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            mMhlDevices = inputs;
1093ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
1094ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
1095ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
1096ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> getMhlDevicesLocked() {
1097ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return mMhlDevices;
1098ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
1099ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
1100b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private class HdmiMhlVendorCommandListenerRecord implements IBinder.DeathRecipient {
1101b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        private final IHdmiMhlVendorCommandListener mListener;
1102f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
1103b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener) {
1104f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mListener = listener;
1105f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
1106f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
1107f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        @Override
1108f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        public void binderDied() {
1109b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.remove(this);
1110f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
1111f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
1112f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
111378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
111478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
111578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
111678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
111778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
111878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
111978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
112078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
112178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
112278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
112378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
112478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
112578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
112678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
112778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
11283cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim
11293cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        @Override
11303cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        public boolean equals(Object obj) {
11313cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            if (!(obj instanceof HotplugEventListenerRecord)) return false;
11323cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            if (obj == this) return true;
11333cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            HotplugEventListenerRecord other = (HotplugEventListenerRecord) obj;
11343cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            return other.mListener == this.mListener;
11353cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        }
11363cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim
11373cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        @Override
11383cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        public int hashCode() {
11393cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            return mListener.hashCode();
11403cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        }
114178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
114278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
11436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
11446d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
11456d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11466d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
11476d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
11486d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11496d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
1151ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
11526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
11536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
11546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
11556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11566d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
11576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1158ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
115938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
1160ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1161ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
1162ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
1163ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1164ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1165ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1166ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
1167ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
1168ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
1169ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1170ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1171ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1172ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1173119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
1174119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
1175119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
1176119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1177119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
1178119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
1179119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
1180119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1181119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1182119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1183119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
1184119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
1185119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
1186119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1187119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1188119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1189119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
119012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
1191f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiRecordListener mListener;
1192f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1193f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public HdmiRecordListenerRecord(IHdmiRecordListener listener) {
1194f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1195f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1196f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1197b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1198b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
1199b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
1200fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                if (mRecordListenerRecord == this) {
1201fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                    mRecordListenerRecord = null;
1202fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                }
1203b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1204b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1205b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1206b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
120778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
120878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
120978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
121078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
121178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
121278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
121378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
121478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
12150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
12160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
12170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
12180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
121978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
12200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
122178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
122278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
122378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
122461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
1225b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
12267e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
12277e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
12287e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
12297e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
12307e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
12317e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
12327e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
123361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
123461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
123561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
12367e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
12377e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
123861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
12397640d9895cf8fae7a99a7db5bba0079ba6022621Jinsuk Kim                HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
1240d47abefc8269dae7fdfa2bb102bcb89cbea7c7b0Jinsuk Kim                return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
12417e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
12427e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
12437e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
12447e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
12457e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
12468960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
1247a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
1248a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
1249a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
1250a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
125172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
125272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
125372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
125472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
125579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1256a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
1257f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                        if (!mAddressAllocated) {
1258f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                            mSelectRequestBuffer.set(SelectRequestBuffer.newDeviceSelect(
1259f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                                    HdmiControlService.this, deviceId, callback));
1260f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                            return;
1261f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                        }
1262a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1263c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1264a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
1265a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
12663b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
1267f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1268f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        if (device.getPortId() == tv.getActivePortId()) {
1269f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
127087f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            return;
127187f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        }
1272f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // Upon selecting MHL device, we send RAP[Content On] to wake up
1273f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // the connected mobile device, start routing control to switch ports.
1274f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // callback is handled by MHL action.
1275f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.turnOn(callback);
12767c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                        tv.doManualPortSwitching(device.getPortId(), null);
1277f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
127887f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    }
12798960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                    tv.deviceSelect(deviceId, callback);
1280a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
1281a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
1282a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
1283a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
1284a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
1285a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
1286a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1287a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1288a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1289a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
129072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
129172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
129272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
129372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
1294a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
1295a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
1296f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                        if (!mAddressAllocated) {
1297f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                            mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
1298f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                                    HdmiControlService.this, portId, callback));
1299f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                            return;
1300f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim                        }
1301a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1302c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1303a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1304a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
13058333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
1306a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1307a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1308a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1309a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1310a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
1311c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
1312a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1313a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1314a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1315a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
13163b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(mActivePortId);
1317f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1318f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.sendKeyEvent(keyCode, isPressed);
1319f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
13204612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    }
13214612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mCecController != null) {
13224612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
13234612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (localDevice == null) {
13244612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            Slog.w(TAG, "Local device not available");
13254612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
13264612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
13274612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        localDevice.sendKeyEvent(keyCode, isPressed);
1328a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1329a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1330a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1331a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1332a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1333a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
13347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
133578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
13367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
13377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
13387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
13397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
13407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
13417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
134278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
134378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
134478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
13457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
134678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
13477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
13487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
13497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
13507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
13517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
13527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
135378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
135478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
135578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
13567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
135778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1358f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addHotplugEventListener(listener);
135978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
136078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
136178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
13627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
136378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1364f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.removeHotplugEventListener(listener);
136578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
13666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
13676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
13686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
13696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
1370f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addDeviceEventListener(listener);
13716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
13726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
13736d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
13746d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
13756d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
13762738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang            return HdmiControlService.this.getPortInfo();
13776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1378ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1379ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1380ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1381ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1382ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1383ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1384ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1385ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1386e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1387ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1388ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1389ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1390ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1391ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1392ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1393ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1394ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1395ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1396377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1397ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1398ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1399ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1400ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1401ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1402ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1403ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1404ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1405ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1406ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1407ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1408c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1409ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1410ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1411ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1412ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1413ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1414ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1415ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1416ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1417ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1418ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1419ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1420ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1421ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1422ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1423ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1424ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1425ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1426ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1427ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1428ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
142992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
143092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
14319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
14329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
14339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
14349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
14369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
143761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
14389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
14399c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
14409c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
14419c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1442ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            synchronized (mLock) {
1443ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                List<HdmiDeviceInfo> cecDevices = (tv == null)
1444ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1445ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        : tv.getSafeExternalInputsLocked();
1446ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
14479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
14489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
14499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1450bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // Returns all the CEC devices on the bus including system audio, switch,
1451bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // even those of reserved type.
1452bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        @Override
1453bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        public List<HdmiDeviceInfo> getDeviceList() {
1454bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            enforceAccessPermission();
1455bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1456bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            synchronized (mLock) {
1457bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                return (tv == null)
1458bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1459bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        : tv.getSafeCecDevicesLocked();
1460bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            }
1461bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        }
1462bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim
14639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
146441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
146541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
146641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
146741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
146841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
146941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
147041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
147141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
147241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
147341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
147441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
147541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
147641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
147741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
147841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
147941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
148041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
148141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
148241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
148341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
148441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
148541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
148641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
148741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
148841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
148941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
149041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
149141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
149241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
149341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
149441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
149541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
149641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1497a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1498a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1499a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1500a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1501a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1502a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1503a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
150438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1505a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1506a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1507a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1508a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1509a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1510160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1511160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
15124d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
15134d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
15144d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
15154d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
15164d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
15174d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
15184d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1519119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1520119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1521119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1522119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1523119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1524f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1525119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1526119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1527119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1528119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1529119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1530119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1531119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1532119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1533119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1534119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1535119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1536119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1537119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1538119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1539119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1540119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1541119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1542119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1543119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1544119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1545119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1546119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1547119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1548119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
154912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1550a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1551a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1552d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        public void sendStandby(final int deviceType, final int deviceId) {
1553d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            enforceAccessPermission();
1554d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            runOnServiceThread(new Runnable() {
1555d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                @Override
1556d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                public void run() {
155761c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim                    HdmiMhlLocalDeviceStub mhlDevice = mMhlController.getLocalDeviceById(deviceId);
155861c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim                    if (mhlDevice != null) {
155961c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim                        mhlDevice.sendStandby();
156061c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim                        return;
156161c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim                    }
1562d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1563d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    if (device == null) {
1564d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        Slog.w(TAG, "Local device not available");
1565d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        return;
1566d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    }
1567d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    device.sendStandby(deviceId);
1568d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                }
1569d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            });
1570d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        }
1571d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim
1572d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        @Override
157312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
1574b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
157512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1576b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1577b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1578b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1579b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1580b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1581b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1582b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1583b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1584de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    if (!isTvDeviceEnabled()) {
1585de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                        Slog.w(TAG, "TV device is not enabled.");
1586b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1587b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1588b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1589b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1590b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1591b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1592b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1593b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1594b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1595b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1596b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1597b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1598b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1599de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    if (!isTvDeviceEnabled()) {
1600de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                        Slog.w(TAG, "TV device is not enabled.");
1601b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1602b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1603b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1604b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1605b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1606a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1607a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1608a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1609b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1610b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1611b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1612b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1613b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1614b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1615de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    if (!isTvDeviceEnabled()) {
1616de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                        Slog.w(TAG, "TV device is not enabled.");
1617b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1618b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1619b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1620b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1621b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1622bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1623bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1624bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1625b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1626b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1627b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1628b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1629b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1630b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1631de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                    if (!isTvDeviceEnabled()) {
1632de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim                        Slog.w(TAG, "TV device is not enabled.");
1633b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1634b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1635b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1636b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1637b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1638a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1639f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1640f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1641b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void sendMhlVendorCommand(final int portId, final int offset, final int length,
1642f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                final byte[] data) {
1643f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1644f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            runOnServiceThread(new Runnable() {
1645f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                @Override
1646f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                public void run() {
1647f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (!isControlEnabled()) {
1648f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Hdmi control is disabled.");
1649f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return ;
1650f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
16513b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
1652f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (device == null) {
1653f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Invalid port id:" + portId);
1654f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return;
1655f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
1656b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    mMhlController.sendVendorCommand(portId, offset, length, data);
1657f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1658f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            });
1659f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1660f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1661f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1662b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void addHdmiMhlVendorCommandListener(
1663b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                IHdmiMhlVendorCommandListener listener) {
1664f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1665b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            HdmiControlService.this.addHdmiMhlVendorCommandListener(listener);
1666f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1667959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1668959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        @Override
1669b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        public void setStandbyMode(final boolean isStandbyModeOn) {
1670b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            enforceAccessPermission();
1671b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            runOnServiceThread(new Runnable() {
1672b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                @Override
1673b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                public void run() {
1674b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                    HdmiControlService.this.setStandbyMode(isStandbyModeOn);
1675b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                }
1676b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            });
1677b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        }
1678b3515648fb76a04a218666f166e0a26549343178Donghyun Cho
1679b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        @Override
1680959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1681fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey            if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) return;
1682959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1683959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1684959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
1685959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mProhibitMode: " + mProhibitMode);
1686959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            if (mCecController != null) {
1687959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("mCecController: ");
1688959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.increaseIndent();
1689959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                mCecController.dump(pw);
1690959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.decreaseIndent();
1691959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
169261c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim
169361c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim            pw.println("mMhlController: ");
169461c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim            pw.increaseIndent();
169561c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim            mMhlController.dump(pw);
169661c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim            pw.decreaseIndent();
169761c94d1a03971b07c4ac28af678f3fff6b695c32Jinsuk Kim
1698959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPortInfo: ");
1699959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.increaseIndent();
1700959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
1701959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("- " + hdmiPortInfo);
1702959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1703959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.decreaseIndent();
1704959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPowerStatus: " + mPowerStatus);
1705959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
170678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
170778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1708a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
170979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
171079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
171179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
17127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
17137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1714c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
17157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
17167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
171779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
171878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
171978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1720a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
172179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
172279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
172379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
17247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
17257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1726c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
17277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
17287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
172979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
173078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
173178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
17323cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim    private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
17333cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
173478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
173578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
173678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
173778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
173878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
173978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
174078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
174178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
174278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
17433cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim
17443cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        // Inform the listener of the initial state of each HDMI port by generating
17453cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        // hotplug events.
17463cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        runOnServiceThread(new Runnable() {
17473cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            @Override
17483cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            public void run() {
17493cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                synchronized (mLock) {
17503cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                    if (!mHotplugEventListenerRecords.contains(record)) return;
17513cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                }
17523cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                for (HdmiPortInfo port : mPortInfo) {
17533cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                    HdmiHotplugEvent event = new HdmiHotplugEvent(port.getId(),
17543cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                            mCecController.isConnected(port.getId()));
17553cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                    synchronized (mLock) {
17563cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                        invokeHotplugEventListenerLocked(listener, event);
17573cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                    }
17583cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim                }
17593cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim            }
17603cd30516ad16150dee55ad8f704a0c3bf20b58b2Jinsuk Kim        });
176178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
176278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
176378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
176478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
176578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
176678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
176778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
176878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
176978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
177078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
177178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
177278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
177378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
17747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
17756d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
17764893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
17774893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
17784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
17794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
17804893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
17814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
17824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
17836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
17844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
17854893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
17864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
17874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
178861daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
17894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
1790f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
17914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
1792f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onStatusChanged(device, status);
17934893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
17944893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
17956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
17966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
17976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
17986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
17996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1800ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1801ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1802ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1803ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1804ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1805ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1806ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1807ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1808ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1809ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1810ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1811ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1812ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1813ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1814ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1815ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1816ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1817ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1818ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1819ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1820ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1821ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1822ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1823ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1824ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1825ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1826ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
18279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
1828f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiInputChangeListener mListener;
1829f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1830f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public InputChangeListenerRecord(IHdmiInputChangeListener listener) {
1831f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1832f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1833f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
18349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
18359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
18369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
1837fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                if (mInputChangeListenerRecord == this) {
1838fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                    mInputChangeListenerRecord = null;
1839fbbeb3e54a73e0fa7dbd5c2c1093705e69ae27daDonghyun Cho                }
18409c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
18419c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
18429c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
18439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
18449c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
18459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1846f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mInputChangeListenerRecord = new InputChangeListenerRecord(listener);
18479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
18489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
18499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
18509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
18519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
18529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
18539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
18549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
18559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
185661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
18579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1858f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mInputChangeListenerRecord != null) {
18599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
1860f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mInputChangeListenerRecord.mListener.onChanged(info);
18619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
18629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
18639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
18649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
18659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
18669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
18679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
186812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1869b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1870f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord(listener);
1871b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
187212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1873b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
187412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1875b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1876b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1877b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1878b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1879b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1880b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1881f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
188212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1883f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress);
188412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
188512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1886b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1887b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1888b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1889b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1890b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1891b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1892326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeOneTouchRecordResult(int recorderAddress, int result) {
189312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1894f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
189512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1896326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onOneTouchRecordResult(recorderAddress, result);
189712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
189812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
189912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
190012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
190112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
190212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
190312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
1904326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeTimerRecordingResult(int recorderAddress, int result) {
190512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1906f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
190712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1908326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onTimerRecordingResult(recorderAddress, result);
190912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1910e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1911e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1912e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1913e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1914e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1915e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1916326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeClearTimerRecordingResult(int recorderAddress, int result) {
1917e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1918f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
1919e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1920326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onClearTimerRecordingResult(recorderAddress,
1921326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                            result);
1922e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1923e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
192412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
192512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
192612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
192712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
192812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
19297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
19307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
19317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
19327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
19337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
19347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
19357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
193663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1937f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener,
1938ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1939ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1940ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1941ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1942ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1943ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1944ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1945ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
19464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
19474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
194860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
1949f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
1950f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeHotplugEventListenerLocked(record.mListener, event);
195160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
195260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
195360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
195460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
19554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
195660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
195760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
195860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
195960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
196060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
196160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1962e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1963e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
1964f98b9e87f9258d4ffb53859de2a138e72174cc23Jinsuk Kim    public HdmiCecLocalDeviceTv tv() {
196561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
196679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
196779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1968e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1969b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
1970e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1971e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
1972de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim    boolean isTvDeviceEnabled() {
1973de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim        return isTvDevice() && tv() != null;
1974de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim    }
1975de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim
197679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1977c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
197861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
197960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1980a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1981a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1982a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1983a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
198492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
198592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
198692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
198792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
198892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
198992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
199038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1991f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
199238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
1993f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
199438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
199538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
199638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1997f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
199838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1999f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
2000c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
2001c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
200238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
200338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
2004f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
200538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
2006f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
2007c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
2008c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
200938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
201038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
2011f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
201238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
2013f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
2014c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
201538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
201638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
201738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
201838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
201938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
2020fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
2021280a64e793d081847c5dcea23ed9be38aa5332d2Dianne Hackborn        mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
202238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
202338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
202438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
202538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
202638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
202738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
202838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
202902920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho        if (!canGoToStandby()) {
203002920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho            return;
203102920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho        }
203238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
2033e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
203438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
203538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
203638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
203738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
2038afd26a250777521f692cbc2553349210f69a1bc1Donghyun Cho    boolean isWakeUpMessageReceived() {
2039afd26a250777521f692cbc2553349210f69a1bc1Donghyun Cho        return mWakeUpMessageReceived;
2040afd26a250777521f692cbc2553349210f69a1bc1Donghyun Cho    }
2041afd26a250777521f692cbc2553349210f69a1bc1Donghyun Cho
204238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
204338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
204438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
2045c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
204638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
2047a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
2048fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
2049fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
2050fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
2051fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
2052fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
2053a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
205438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
205538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
205638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
205738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
205838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
205938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
206038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
2061e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    private void onStandby(final int standbyAction) {
206238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
2063c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
20640608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        invokeVendorCommandListenersOnControlStateChanged(false,
20650608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
206602920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho        if (!canGoToStandby()) {
206702920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho            mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
206802920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho            return;
206902920a08e129eb58b6c2e35ccda6183ab6503a98Donghyun Cho        }
20704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
20724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
20734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
20744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
20754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
20764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
20774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
2078e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim                    onStandbyCompleted(standbyAction);
20794b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
20804b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
20814b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
20824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
20834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
20844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
20854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
20864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
2087e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    private boolean canGoToStandby() {
2088e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
2089e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim            if (!device.canGoToStandby()) return false;
2090e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        }
2091e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim        return true;
2092e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim    }
2093e26d833c4a00bc7c1c23083f28ef891703e7e385Jinsuk Kim
20941ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
20951ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private void onLanguageChanged(String language) {
20961ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        assertRunOnServiceThread();
20971ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        mLanguage = language;
20981ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2099de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim        if (isTvDeviceEnabled()) {
21001ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            tv().broadcastMenuLanguage(language);
2101bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho            mCecController.setLanguage(language);
21021ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        }
21031ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    }
21041ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2105f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
2106f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    String getLanguage() {
2107f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
2108f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        return mLanguage;
2109f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    }
2110f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang
21114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
2112350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang        if (mCecController != null) {
2113350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
2114350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                device.disableDevice(mStandbyMessageReceived, callback);
2115350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            }
211638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
2117350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang
2118f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.clearAllLocalDevices();
211938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
212038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
212138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
21224fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
21234fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
21244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
21254fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
21264fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
21274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
21284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
21294fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
21304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
21314fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
2132e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim    private void onStandbyCompleted(int standbyAction) {
213338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
21344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
21354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
2136c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
213738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
213838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
2139c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
214038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
2141e6e8f3d589f42393cf02a2bd766d678d80dad874Jinsuk Kim            device.onStandby(mStandbyMessageReceived, standbyAction);
214238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
214338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
2144bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
21455b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim        mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
214638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
21474d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
2148119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
2149119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
2150119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
2151119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
2152119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
2153119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
2154119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
2155119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
2156119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
2157119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
2158119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
2159119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
2160119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
21610608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress,
21620608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            byte[] params, boolean hasVendorId) {
2163119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
2164d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            if (mVendorCommandListenerRecords.isEmpty()) {
2165d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                return false;
2166d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            }
2167119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
2168119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
2169119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
2170119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
2171119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
21720608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
2173119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
2174119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
2175119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
2176119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
2177d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            return true;
2178119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
2179119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
2180119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
21810608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) {
21820608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        synchronized (mLock) {
21830608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            if (mVendorCommandListenerRecords.isEmpty()) {
21840608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                return false;
21850608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
21860608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
21870608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                try {
21880608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onControlStateChanged(enabled, reason);
21890608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                } catch (RemoteException e) {
21900608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e);
21910608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                }
21920608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
21930608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            return true;
21940608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
21950608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    }
21960608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
2197b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
2198b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        HdmiMhlVendorCommandListenerRecord record =
2199b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                new HdmiMhlVendorCommandListenerRecord(listener);
2200f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        try {
2201f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            listener.asBinder().linkToDeath(record, 0);
2202f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        } catch (RemoteException e) {
2203f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            Slog.w(TAG, "Listener already died.");
2204f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            return;
2205f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
2206f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2207f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
2208b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.add(record);
2209f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
2210f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
2211f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2212b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    void invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data) {
2213f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
2214b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            for (HdmiMhlVendorCommandListenerRecord record : mMhlVendorCommandListenerRecords) {
2215f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                try {
2216f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onReceived(portId, offest, length, data);
2217f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                } catch (RemoteException e) {
2218b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    Slog.e(TAG, "Failed to notify MHL vendor command", e);
2219f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
2220f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
2221f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
2222f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
2223f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2224b3515648fb76a04a218666f166e0a26549343178Donghyun Cho    void setStandbyMode(boolean isStandbyModeOn) {
2225b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        assertRunOnServiceThread();
2226b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        if (isPowerOnOrTransient() && isStandbyModeOn) {
2227b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            mPowerManager.goToSleep(SystemClock.uptimeMillis(),
2228b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                    PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
2229b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            if (playback() != null) {
2230b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                playback().sendStandby(0 /* unused */);
2231b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            }
2232b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        } else if (isPowerStandbyOrTransient() && !isStandbyModeOn) {
2233b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
2234b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            if (playback() != null) {
2235b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                oneTouchPlay(new IHdmiControlCallback.Stub() {
2236b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                    @Override
2237b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                    public void onComplete(int result) {
2238b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                        if (result != HdmiControlManager.RESULT_SUCCESS) {
2239b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                            Slog.w(TAG, "Failed to complete 'one touch play'. result=" + result);
2240b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                        }
2241b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                    }
2242b3515648fb76a04a218666f166e0a26549343178Donghyun Cho                });
2243b3515648fb76a04a218666f166e0a26549343178Donghyun Cho            }
2244b3515648fb76a04a218666f166e0a26549343178Donghyun Cho        }
2245b3515648fb76a04a218666f166e0a26549343178Donghyun Cho    }
2246b3515648fb76a04a218666f166e0a26549343178Donghyun Cho
22474d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
22484d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
22494d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
22504d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
22514d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
22524d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
22534d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
22544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
22554d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
22564d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
22574d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
22584fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
22594fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
2260bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho    void setCecOption(int key, boolean value) {
22615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
22625008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
22635008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
22645008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
22655008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
22665008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
22674fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
22684fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
22694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
22704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
22714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
22724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
22734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
2274f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            enableHdmiControlService();
2275f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            return;
22764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
2277f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        // Call the vendor handler before the service is disabled.
2278f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        invokeVendorCommandListenersOnControlStateChanged(false,
2279f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
2280f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        // Post the remained tasks in the service thread again to give the vendor-issued-tasks
2281f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        // a chance to run.
2282f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        runOnServiceThread(new Runnable() {
2283f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            @Override
2284f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            public void run() {
2285f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                disableHdmiControlService();
2286f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            }
2287f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        });
2288f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        return;
2289f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    }
2290f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo
2291f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    @ServiceThreadOnly
2292f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    private void enableHdmiControlService() {
2293bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
2294f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
2295f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo
2296f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        initializeCec(INITIATED_BY_ENABLE_CEC);
2297f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    }
2298f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo
2299f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    @ServiceThreadOnly
2300f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo    private void disableHdmiControlService() {
2301f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        disableDevices(new PendingActionClearedCallback() {
2302f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            @Override
2303f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            public void onCleared(HdmiCecLocalDevice device) {
2304f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                assertRunOnServiceThread();
2305f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                mCecController.flush(new Runnable() {
2306f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                    @Override
2307f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                    public void run() {
2308bc6e372b25d7d62efecefba09304c5a66218c91aDonghyun Cho                        mCecController.setOption(OptionKey.ENABLE_CEC, false);
2309f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                        mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
2310f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                        clearLocalDevices();
2311f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                    }
2312f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo                });
2313f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo            }
2314f17024873b10dabed069e502030dd85d6257c0c4Yuncheol Heo        });
23154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
2316867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
2317867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
2318867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
2319867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
2320867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
2321e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2322e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // Resets last input for MHL, which stays valid only after the MHL device was selected,
2323e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // and no further switching is done.
2324e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        setLastInputForMhl(Constants.INVALID_PORT_ID);
2325e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2326e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2327e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2328e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void setLastInputForMhl(int portId) {
2329e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2330e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mLastInputMhl = portId;
2331e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2332e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2333e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2334e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    int getLastInputForMhl() {
2335e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2336e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        return mLastInputMhl;
2337e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2338e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2339e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    /**
2340e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * Performs input change, routing control for MHL device.
2341e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     *
2342e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param portId MHL port, or the last port to go back to if {@code contentOn} is false
2343e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param contentOn {@code true} if RAP data is content on; otherwise false
2344e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     */
2345e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2346e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void changeInputForMhl(int portId, boolean contentOn) {
2347e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2348de7a4248d8631099544e4cf43c02b10131cf6672Jinsuk Kim        if (tv() == null) return;
2349e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
2350cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
2351cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim            tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
2352cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                @Override
2353cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                public void onComplete(int result) throws RemoteException {
2354cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                    // Keep the last input to switch back later when RAP[ContentOff] is received.
2355cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                    // This effectively sets the port to invalid one if the switching is for
2356cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                    // RAP[ContentOff].
2357cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                    setLastInputForMhl(lastInput);
2358cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                }
2359cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim            });
2360cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim        }
2361e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // MHL device is always directly connected to the port. Update the active port ID to avoid
2362e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // unnecessary post-routing control task.
2363e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().setActivePortId(portId);
2364e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2365e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // The port is either the MHL-enabled port where the mobile device is connected, or
2366f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // the last port to go back to when turnoff command is received. Note that the last port
2367e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // may not be the MHL-enabled one. In this case the device info to be passed to
2368e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // input change listener should be the one describing the corresponding HDMI port.
23693b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
2370cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim        HdmiDeviceInfo info = (device != null) ? device.getInfo()
2371cb8661c08f4a7b00eaa2ede06a30c32dd3cbc53bJinsuk Kim                : mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
2372e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        invokeInputChangeListener(info);
2373867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
237408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
2375e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim   void setMhlInputChangeEnabled(boolean enabled) {
2376f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim       mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
237708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
237808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
237908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
238008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
238108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
238208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
238308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
238408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
238508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
238608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
238708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
2388339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang
2389339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    @ServiceThreadOnly
2390339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    void displayOsd(int messageId) {
2391339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        assertRunOnServiceThread();
2392339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
2393339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
2394339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
2395339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                HdmiControlService.PERMISSION);
2396339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    }
23972e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang
23982e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    @ServiceThreadOnly
23992e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    void displayOsd(int messageId, int extra) {
24002e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        assertRunOnServiceThread();
24012e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
24022e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
24032b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
24042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
24052e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiControlService.PERMISSION);
24062e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    }
24070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
2408