HdmiControlService.java revision 326aef0c9402742e29c4503c857f93e75cf9a6ec
10792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/*
20792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Copyright (C) 2014 The Android Open Source Project
30792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
40792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License");
50792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * you may not use this file except in compliance with the License.
60792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * You may obtain a copy of the License at
70792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
80792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *      http://www.apache.org/licenses/LICENSE-2.0
90792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang *
100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Unless required by applicable law or agreed to in writing, software
110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS,
120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * See the License for the specific language governing permissions and
140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * limitations under the License.
150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpackage com.android.server.hdmi;
180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
19ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
20ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kimimport static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.DISABLED;
225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.ENABLED;
235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
275008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
285008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
3138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
327ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
3438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
3538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.database.ContentObserver;
37c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
387d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.hardware.hdmi.HdmiDeviceInfo;
3960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
41d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
42d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
44d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
46b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kimimport android.hardware.hdmi.IHdmiMhlVendorCommandListener;
4712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
48ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
49119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
50a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
515008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.net.Uri;
5242c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
5367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
5578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
56e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
5738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
5878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
5938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
607d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.os.SystemProperties;
615008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kimimport android.os.UserHandle;
627ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
637d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heoimport android.text.TextUtils;
642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
650792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
678b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
6878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
694893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
70959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport com.android.internal.util.IndentingPrintWriter;
710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
72a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
747e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
77b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
78b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
79959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.FileDescriptor;
80959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heoimport java.io.PrintWriter;
8178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
82f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
851ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heoimport java.util.Locale;
86a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
870792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
880792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
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
105d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
106d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
107d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
108d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
109d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
110d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
111d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
112ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
1134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
1144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
1154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
1164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
1174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
118d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
119d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
120d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
121d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
12202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
12302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
12402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
12502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
12602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
12702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
12802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
12902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
13002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
13102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
13202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
13302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
1341ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private class HdmiControlBroadcastReceiver extends BroadcastReceiver {
135f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        @ServiceThreadOnly
13638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
13738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
138f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang            assertRunOnServiceThread();
13938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
14038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
14138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
14238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
14338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
14438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
14538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
14638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
14738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
14838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
14938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
1501ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                case Intent.ACTION_CONFIGURATION_CHANGED:
1515fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                    String language = getMenuLanguage();
1521ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    if (!mLanguage.equals(language)) {
1531ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                        onLanguageChanged(language);
1541ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    }
1551ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo                    break;
15638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
15738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1585fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim
1595fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        private String getMenuLanguage() {
1605fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            Locale locale = Locale.getDefault();
1615fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) {
1625fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Android always returns "zho" for all Chinese variants.
1635fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Use "bibliographic" code defined in CEC639-2 for traditional
1645fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                // Chinese used in Taiwan/Hong Kong/Macau.
1655fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return "chi";
1665fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            } else {
1675fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim                return locale.getISO3Language();
1685fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim            }
1695fe3a6cbfc50a8faf9cf051f1f09a7eb1ef6b7eeJinsuk Kim        }
17038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
17138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1740792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1750792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
17778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
17878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
17978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1800340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1810340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
18278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
18378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
18578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
18678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
18778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
188f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for device event listener to handle the caller killed in action.
1894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
193f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    // List of records for vendor command listener to handle the caller killed in action.
194119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
195119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
196119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
197119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1999c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
2009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
201b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
20212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
203b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
20492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
20592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
20692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
20792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
20892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
2094d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
2104d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
2114d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
2124d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
2134d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
2144d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
215ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
216ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
217ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
218ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
2194893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
2210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private final SettingsObserver mSettingsObserver;
2235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
224f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private final HdmiControlBroadcastReceiver
225f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver();
226f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
2270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2342b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
23530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2362b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2372b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
23830c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2392b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
240e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Map from port ID to HdmiDeviceInfo.
241e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
242e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
24375a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
24475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
24538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
246c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
24738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
24838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
2491ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private String mLanguage = Locale.getDefault().getISO3Language();
2501ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
2511ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
25238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
25338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
254fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
255fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
256fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
257867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
258867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    private int mActivePortId = Constants.INVALID_PORT_ID;
259867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
260f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Set to true while the input change by MHL is allowed.
261f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
262f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private boolean mMhlInputChangeEnabled;
263f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
264b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    // List of records for MHL Vendor command listener to handle the caller killed in action.
265f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
266b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private final ArrayList<HdmiMhlVendorCommandListenerRecord>
267b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords = new ArrayList<>();
268f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
269f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @GuardedBy("mLock")
270f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    private List<HdmiDeviceInfo> mMhlDevices;
271f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
272f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    @Nullable
273781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim    private HdmiMhlControllerStub mMhlController;
274f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
275f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // Last input port before switching to the MHL port. Should switch back to this port
276f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    // when the mobile device sends the request one touch play with off.
277e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    // Gets invalidated if we go to other port/input.
278e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
279e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    private int mLastInputMhl = Constants.INVALID_PORT_ID;
280e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2810792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2820792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2837d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
2845008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mSettingsObserver = new SettingsObserver(mHandler);
2850792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2860792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2877d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    private static List<Integer> getIntList(String string) {
2887d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        ArrayList<Integer> list = new ArrayList<>();
2897d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
2907d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        splitter.setString(string);
2917d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        for (String item : splitter) {
2927d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            try {
2937d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                list.add(Integer.parseInt(item));
2947d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            } catch (NumberFormatException e) {
2957d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo                Slog.w(TAG, "Can't parseInt: " + item);
2967d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo            }
2977d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        }
2987d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo        return Collections.unmodifiableList(list);
2997d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo    }
3007d9acc7a3eddb3e57c0b8312c3baf7ebb4f529d9Yuncheol Heo
3010792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
3020792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
3032f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
304c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
3057ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
3067ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
30708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
3088b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
309a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
3103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
311347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
3125008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
313347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
314a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
315a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
316fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
317a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
318a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
3190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
32008f1ab02d6de42756825a2dfa7027137ff959bd8Jinsuk Kim            return;
3210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
3220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
323781041239f2931ca16c902fb371cd041b057c918Jinsuk Kim        mMhlController = HdmiMhlControllerStub.create(this);
324f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (!mMhlController.isReady()) {
3250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
3260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
327ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        mMhlDevices = Collections.emptyList();
328f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
329f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        initPortInfo();
33075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
3318692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
33263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
333f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mCecController != null) {
3340608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register broadcast receiver for power state change.
33538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
33638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
33738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
3381ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
3391ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
3400608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
3410608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Register ContentObserver to monitor the settings change.
3420608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            registerContentObserver();
34338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
3447ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
34538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
34625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
34725c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
34825c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
3490608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    private void onInitializeCecComplete(int initiatedBy) {
350fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
351fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
352fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
353fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
354fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
35525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
3565008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
3570608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
3580608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        int reason = -1;
3590608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        switch (initiatedBy) {
3600608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_BOOT_UP:
3610608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START;
3620608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3630608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_ENABLE_CEC:
3640608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING;
3650608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3660608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_SCREEN_ON:
3670608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            case INITIATED_BY_WAKE_UP_MESSAGE:
3680608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP;
3690608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                break;
3700608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
3710608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (reason != -1) {
3720608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            invokeVendorCommandListenersOnControlStateChanged(true, reason);
37325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
37425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
37525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
3765008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private void registerContentObserver() {
3775008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        ContentResolver resolver = getContext().getContentResolver();
3785008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        String[] settings = new String[] {
3795008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_ENABLED,
3805008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
3815008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
3825008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_INPUT_SWITCHING_ENABLED,
3835008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                Global.MHL_POWER_CHARGE_ENABLED
3845008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        };
3855691b2f2297b29dc83a7f83f77da517035b11cceJungshik Jang        for (String s : settings) {
3865008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
3875008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    UserHandle.USER_ALL);
3885008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
3905008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
3915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private class SettingsObserver extends ContentObserver {
3925008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public SettingsObserver(Handler handler) {
3935008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            super(handler);
3945008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
3955008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
396f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        // onChange is set up to run in service thread.
3975008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        @Override
3985008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        public void onChange(boolean selfChange, Uri uri) {
3995008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            String option = uri.getLastPathSegment();
4005008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            boolean enabled = readBooleanSetting(option, true);
4015008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            switch (option) {
4025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_ENABLED:
4035008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    setControlEnabled(enabled);
4045008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4055008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
4065008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoWakeup(enabled);
407350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                    setCecOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
4085008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4095008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
4105008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    tv().setAutoDeviceOff(enabled);
4115008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    // No need to propagate to HAL.
4125008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4135008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_INPUT_SWITCHING_ENABLED:
41408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo                    setMhlInputChangeEnabled(enabled);
4155008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4165008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                case Global.MHL_POWER_CHARGE_ENABLED:
417f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
4185008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim                    break;
4195008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim            }
4205008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        }
4215008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4225008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4235008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    private static int toInt(boolean enabled) {
4245008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return enabled ? ENABLED : DISABLED;
4255008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4265008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
4277ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
4287ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
4295008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
4307ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
4317ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
4327ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
4337ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
4345008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        Global.putInt(cr, key, toInt(value));
4355008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
4365008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
437fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
4385008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
439b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        initializeLocalDevices(initiatedBy);
440a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
441a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
442a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
443b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void initializeLocalDevices(final int initiatedBy) {
444a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
445b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        // A container for [Device type, Local device info].
446b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
447b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (int type : mLocalDevices) {
4486f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
4496f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            if (localDevice == null) {
4506f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim                localDevice = HdmiCecLocalDevice.create(this, type);
4516f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim            }
4523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
453b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            localDevices.add(localDevice);
454b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
4556f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // It's now safe to flush existing local devices from mCecController since they were
4566f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        // already moved to 'localDevices'.
4576f87b4e6b6db76cb32d449ad1fdf1946ff4e96f7Jinsuk Kim        clearLocalDevices();
458b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        allocateLogicalAddress(localDevices, initiatedBy);
459b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    }
460b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
461b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    @ServiceThreadOnly
462b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void allocateLogicalAddress(final ArrayList<HdmiCecLocalDevice> allocatingDevices,
463b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            final int initiatedBy) {
464b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        assertRunOnServiceThread();
46589ec14e48f4a1bdf291cda9fba7b8172f55a2447Yuncheol Heo        mCecController.clearLogicalAddress();
466b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final ArrayList<HdmiCecLocalDevice> allocatedDevices = new ArrayList<>();
467b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        final int[] finished = new int[1];
468b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
469b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            mCecController.allocateLogicalAddress(localDevice.getType(),
4703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
4713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
4723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
473c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
4743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
4753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
476410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
477410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
478410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
479410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
4803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
4813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
4823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
483b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        allocatedDevices.add(localDevice);
4843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
487b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                    if (allocatingDevices.size() == ++finished[0]) {
488b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        if (initiatedBy != INITIATED_BY_HOTPLUG) {
489b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // In case of the hotplug we don't call onInitializeCecComplete()
490b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                            // since we reallocate the logical address only.
4910608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                            onInitializeCecComplete(initiatedBy);
492b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        }
493b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo                        notifyAddressAllocated(allocatedDevices, initiatedBy);
4943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
4963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
4973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
4983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
500a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
501b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo    private void notifyAddressAllocated(ArrayList<HdmiCecLocalDevice> devices, int initiatedBy) {
502a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
503b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        for (HdmiCecLocalDevice device : devices) {
504b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            int address = device.getDeviceInfo().getLogicalAddress();
505fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
5063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
5073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
5090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
5100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
511a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5122b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
513a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
5150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
5160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
5170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
5180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
5190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
5200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
5210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
5222b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
5232b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
5242b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
52530c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
52630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
527e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
5282b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
52930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
53030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
531e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
5320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
53330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
53430c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
535e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
5360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
537f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
538f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
539f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : mhlPortInfo) {
540f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (info.isMhlSupported()) {
541f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                mhlSupportedPorts.add(info.getId());
5420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
543f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
5440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
545f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
546f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // cec port info if we do not have have port that supports MHL.
547f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        if (mhlSupportedPorts.isEmpty()) {
548f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
549f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            return;
550f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
551f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
552f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
553f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            if (mhlSupportedPorts.contains(info.getId())) {
554f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
555f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        info.isCecSupported(), true, info.isArcSupported()));
556f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            } else {
557f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                result.add(info);
5582b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
5592b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
560f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mPortInfo = Collections.unmodifiableList(result);
5610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
5632738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    List<HdmiPortInfo> getPortInfo() {
5642738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang        return mPortInfo;
5652738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang    }
5662738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang
5670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
5680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
5690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
5700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
5710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
5720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
5730340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
5742b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
5750340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
5760340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
577e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
578401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
579401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
580401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
581401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
582401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
583401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
584401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
585c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
586401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
587401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
588401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
589401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
590401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
591401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
592401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
593401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
594401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
595401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
596401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
597c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
5982b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
599401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
600401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
60109ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
6022b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
60309ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
60409ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
605401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
606e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
607e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
608e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
609e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
610e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
611e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
612e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
613e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
614e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
615e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
616e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
617e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
618e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
619e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
620e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
62167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
622e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
623c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
624c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
6253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
6263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
6273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
6283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
6293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
6313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
6323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
6333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
6343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
6353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
6363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
638a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
63961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
6400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
64179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
64279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
64379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
64479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
6458960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        return tv.getCecDeviceInfo(logicalAddress);
646a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
647a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
6483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
649092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
650092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
651092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
652092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
653092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
654092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
655092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
65660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
65760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
65860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
659339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        int portId = pathToPortId(physicalAddress);
6602b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
6612b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
66260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
66360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
66460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
66560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
66679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
66767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
66867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
66967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
67063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
67163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
67263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
67363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
67463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
67563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
67663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
67763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
67863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
67963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
68067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
681c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
682c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
683c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
684d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
685c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
686a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
687d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
688a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6894c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
6905f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
6915f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
6922e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.error("Invalid message type:" + command);
6935f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
6945f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
6955f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
6965f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
697d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
698d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
699a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
700d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
701a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7025f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
703c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
704c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
7056aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
7066aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
7076aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
7086aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
7096aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
7106aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
7116aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
7126aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
7136aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
7146aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
7156aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
7166aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
717a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
718a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
719a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7204c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        int errorCode = mMessageValidator.isValid(message);
7214c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo        if (errorCode != HdmiCecMessageValidator.OK) {
7224c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            // We'll not response on the messages with the invalid source or destination.
7234c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
7244c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo                maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
7254c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            }
7264c212897813d6c23b2e7ffba4a4c01e13a41b1faYuncheol Heo            return true;
72775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
728092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
729092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
730092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
73179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
73279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
73360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
73460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
735a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
736092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
737a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
738092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
73979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
740c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
741092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
742092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
743092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
74460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
745c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
7462e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang            HdmiLogger.warning("Unhandled cec command:" + message);
7473a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
748092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
749a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
750a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
75167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
75267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
75367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
754ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim     * @param portId hdmi port number where hot plug event issued.
75567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
75667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
757a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
758ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    void onHotplug(int portId, boolean connected) {
75960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
760b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
761b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        if (connected && !isTvDevice()) {
762b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
763b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            for (int type : mLocalDevices) {
764b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
765b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                if (localDevice == null) {
766b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice = HdmiCecLocalDevice.create(this, type);
767b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                    localDevice.init();
768b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                }
769b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo                localDevices.add(localDevice);
770b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo            }
771b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo            allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
772b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo        }
773b502186ab34a4de40f1d98591a0c5d8b4eac76e2Yuncheol Heo
77479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
775ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            device.onHotplug(portId, connected);
77660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
777ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
77867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
77967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
78002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
78102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
78202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
78302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
78402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
7851de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
7860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
78702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
7880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
78902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
790a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
7911de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
7921de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
793a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
7941de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
7951de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
7960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
7970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
7980f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
799c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
8000f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
8010f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
8020f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
803c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
8040f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
8050f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
8060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
8070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
80802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
80902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
81060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
81160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
81260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
81360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
81460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
81579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
81679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
81779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
81879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
81979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
820b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
821b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
822b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
823b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
824b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
825b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
826b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
827b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
828b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
829b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
830b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
831b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
832b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
8331a6be6ed3962735f12dbd5ce1bca758120c8fb8dJungshik Jang                    AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
834b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
8353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
8363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
837ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
838f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
839f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (SystemAudioModeChangeListenerRecord record :
840f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mSystemAudioModeChangeListenerRecords) {
841f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeSystemAudioModeChangeLocked(record.mListener, enabled);
842f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
843ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
844ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
845ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
846410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
84742c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
84842c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
84961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
8502b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
8512b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
8523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
8533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
8547df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
8557df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    void handleMhlHotplugEvent(int portId, boolean connected) {
8567df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
85793eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim        // Hotplug event is used to add/remove MHL devices as TV input.
8587df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (connected) {
8593b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub newDevice = new HdmiMhlLocalDeviceStub(this, portId);
8603b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub oldDevice = mMhlController.addLocalDevice(newDevice);
8617df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (oldDevice != null) {
8627df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                oldDevice.onDeviceRemoved();
8637df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.i(TAG, "Old device of port " + portId + " is removed");
8647df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
86593eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            invokeDeviceEventListeners(newDevice.getInfo(), DEVICE_EVENT_ADD_DEVICE);
86693eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim            updateSafeMhlInput();
8677df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
8683b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = mMhlController.removeLocalDevice(portId);
8697df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            if (device != null) {
8707df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                device.onDeviceRemoved();
87193eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE);
87293eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim                updateSafeMhlInput();
8737df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            } else {
8747df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                Slog.w(TAG, "No device to remove:[portId=" + portId);
8757df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang            }
8767df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
877ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        announceHotplugEvent(portId, connected);
8787df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8797df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8807df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
881a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusModeChanged(int portId, int busmode) {
8827df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8833b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
8847df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
885a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setBusMode(busmode);
8867df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
887a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus mode change[portId:" + portId +
888a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim                    ", busmode:" + busmode + "]");
8897df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
8907df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
8917df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
8927df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
893a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlBusOvercurrent(int portId, boolean on) {
8947df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
8953b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
8967df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
897a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.onBusOvercurrentDetected(on);
8987df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
899a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for bus overcurrent event[portId:" + portId + "]");
9007df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
9017df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9027df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
9037df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    @ServiceThreadOnly
904a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim    void handleMhlDeviceStatusChanged(int portId, int adopterId, int deviceId) {
9057df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        assertRunOnServiceThread();
9063b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
907ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
9087df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        if (device != null) {
909a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            device.setDeviceStatusChange(adopterId, deviceId);
9107df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        } else {
911a94417a51646a2560e44974c99435cb00bd96201Jinsuk Kim            Slog.w(TAG, "No mhl device exists for device status event[portId:"
9127df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang                    + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]");
9137df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang        }
9147df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang    }
9157df52862dae1fa33c84725c613b0d9b88c1b28b6Jungshik Jang
916ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    @ServiceThreadOnly
917ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private void updateSafeMhlInput() {
918ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        assertRunOnServiceThread();
919ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<HdmiDeviceInfo> inputs = Collections.emptyList();
9203b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        SparseArray<HdmiMhlLocalDeviceStub> devices = mMhlController.getAllLocalDevices();
921ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        for (int i = 0; i < devices.size(); ++i) {
9223b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim            HdmiMhlLocalDeviceStub device = devices.valueAt(i);
923ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            HdmiDeviceInfo info = device.getInfo();
924ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            if (info != null) {
925ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                if (inputs.isEmpty()) {
926ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                    inputs = new ArrayList<>();
927ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                }
928ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                inputs.add(device.getInfo());
929ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            }
930ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
931ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        synchronized (mLock) {
932ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            mMhlDevices = inputs;
933ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
934ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
935ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
936ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    private List<HdmiDeviceInfo> getMhlDevicesLocked() {
937ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return mMhlDevices;
938ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
939ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
940b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private class HdmiMhlVendorCommandListenerRecord implements IBinder.DeathRecipient {
941b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        private final IHdmiMhlVendorCommandListener mListener;
942f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
943b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public HdmiMhlVendorCommandListenerRecord(IHdmiMhlVendorCommandListener listener) {
944f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim            mListener = listener;
945f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
946f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
947f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        @Override
948f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        public void binderDied() {
949b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.remove(this);
950f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        }
951f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim    }
952f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim
95378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
95478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
95578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
95678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
95778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
95878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
95978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
96078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
96178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
96278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
96378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
96478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
96578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
96678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
96778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
96878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
96978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
9706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
9716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
9726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
9736d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
9746d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
9756d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9766d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
9776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
978ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
9796d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
9806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
9816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
9826d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
9846d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
985ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
98638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
987ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
988ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
989ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
990ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
991ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
992ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
993ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
994ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
995ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
996ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
997ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
998ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
999ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1000119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
1001119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
1002119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
1003119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1004119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
1005119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
1006119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
1007119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1008119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1009119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1010119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
1011119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
1012119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
1013119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1014119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1015119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1016119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
101712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
1018f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiRecordListener mListener;
1019f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1020f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public HdmiRecordListenerRecord(IHdmiRecordListener listener) {
1021f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1022f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1023f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1024b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1025b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
1026b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
1027f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mRecordListenerRecord = null;
1028b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1029b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1030b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1031b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
103278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
103378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
103478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
103578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
103678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
103778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
103878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
103978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
10400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
10410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
10420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
10430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
104478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
10450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
104678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
104778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
104878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
104961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
1050b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
10517e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
10527e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
10537e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
10547e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
10557e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10567e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
10577e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
105861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
105961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
106061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
10617e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10627e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
106361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
106461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
10657e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
10667e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
10677e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
10687e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
10697e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
10708960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
1071a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
1072a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
1073a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
1074a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
107572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
107672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
107772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
107872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
107979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1080a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
1081a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1082c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1083a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
1084a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
10853b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDeviceById(deviceId);
1086f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1087f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        if (device.getPortId() == tv.getActivePortId()) {
1088f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
108987f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                            return;
109087f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                        }
1091f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // Upon selecting MHL device, we send RAP[Content On] to wake up
1092f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // the connected mobile device, start routing control to switch ports.
1093f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        // callback is handled by MHL action.
1094f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.turnOn(callback);
10957c5d31ea93d6f6770c34f7a2a364522d8cc4b5d8Yuncheol Heo                        tv.doManualPortSwitching(device.getPortId(), null);
1096f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
109787f22a2870ac363a5849a7252c1bd44ce2b809c2Jinsuk Kim                    }
10988960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim                    tv.deviceSelect(deviceId, callback);
1099a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
1100a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
1101a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
1102a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
1103a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
1104a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
1105a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1106a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1107a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1108a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
110972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
111072b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
111172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
111272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
1113a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
1114a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
1115a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
1116c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1117a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
1118a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
11198333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
1120a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1121a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1122a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1123a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1124a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
1125c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
1126a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
1127a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
1128a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
1129a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
11303b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(mActivePortId);
1131f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                    if (device != null) {
1132f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        device.sendKeyEvent(keyCode, isPressed);
1133f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim                        return;
11344612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    }
11354612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                    if (mCecController != null) {
11364612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
11374612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        if (localDevice == null) {
11384612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            Slog.w(TAG, "Local device not available");
11394612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                            return;
11404612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        }
11414612a6e1116f1196e6aa64b5a6e3757ea48f94acJungshik Jang                        localDevice.sendKeyEvent(keyCode, isPressed);
1142a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
1143a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
1144a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
1145a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
1146a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
1147a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
11487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
114978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11537fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
11547fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11557fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
115678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
115778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
115878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
116078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
11617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
11627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
11637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
11647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
11657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
11667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
116778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
116878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
116978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
117178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1172f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addHotplugEventListener(listener);
117378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
117478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
117578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
11767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
117778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
1178f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.removeHotplugEventListener(listener);
117978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
11806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11826d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
11836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
1184f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addDeviceEventListener(listener);
11856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11866d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
11876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
11886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
11896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
11902738e2d872a22fe95a99941139863ff642fbd8e7Jungshik Jang            return HdmiControlService.this.getPortInfo();
11916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
1192ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1193ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1194ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
1195ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1196ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1197ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1198ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1199ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1200e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
1201ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1202ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1203ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1204ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
1205ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1206ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
1207ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
1208ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
1209ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1210377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
1211ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1212ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1213ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1214ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
1215ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1216ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
1217ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
1218ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
1219ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1220ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
1221ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
1222c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
1223ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
1224ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
1225ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
1226ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1227ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
1228ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1229ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1230ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1231ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
1232ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1233ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1234ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
1235ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1236ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1237ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
1238ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
1239ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
1240ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
1241ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
1242ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
124392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
124492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
12459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
12469c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
12489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
125161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
12529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
12539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
12549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
12559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1256ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            synchronized (mLock) {
1257ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                List<HdmiDeviceInfo> cecDevices = (tv == null)
1258ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1259ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                        : tv.getSafeExternalInputsLocked();
1260ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim                return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
12619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1264bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // Returns all the CEC devices on the bus including system audio, switch,
1265bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        // even those of reserved type.
1266bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        @Override
1267bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        public List<HdmiDeviceInfo> getDeviceList() {
1268bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            enforceAccessPermission();
1269bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
1270bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            synchronized (mLock) {
1271bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                return (tv == null)
1272bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        ? Collections.<HdmiDeviceInfo>emptyList()
1273bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim                        : tv.getSafeCecDevicesLocked();
1274bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim            }
1275bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim        }
1276bdf27fbf746bee11430c4db2ea6dfd026bae77feJinsuk Kim
12779c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
127841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
127941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
128041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
128141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
128241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
128341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
128441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
128541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
128641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
128741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
128841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
128941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
129041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
129141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
129241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
129341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
129441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
129541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
129641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
129741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
129841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
129941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
130041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
130141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
130241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
130341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
130441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
130541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
130641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
130741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
130841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
130941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
131041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1311a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1312a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1313a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1314a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1315a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1316a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1317a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
131838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1319a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1320a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1321a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1322a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1323a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1324160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1325160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
13264d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
13274d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
13284d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
13294d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
13304d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
13314d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
13324d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1333119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1334119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1335119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1336119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1337119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1338f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1339119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1340119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1341119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1342119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1343119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1344119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1345119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1346119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1347119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1348119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1349119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1350119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1351119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1352119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1353119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1354119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1355119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1356119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1357119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1358119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1359119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1360119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1361119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1362119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
136312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1364a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1365a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1366d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        public void sendStandby(final int deviceType, final int deviceId) {
1367d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            enforceAccessPermission();
1368d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            runOnServiceThread(new Runnable() {
1369d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                @Override
1370d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                public void run() {
1371d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1372d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    if (device == null) {
1373d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        Slog.w(TAG, "Local device not available");
1374d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                        return;
1375d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    }
1376d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                    device.sendStandby(deviceId);
1377d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                }
1378d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            });
1379d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        }
1380d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim
1381d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim        @Override
138212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
1383b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
138412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1385b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1386b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1387b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1388b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1389b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1390b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1391b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1392b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1393b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1394b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1395b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1396b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1397b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1398b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1399b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1400b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1401b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1402b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1403b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1404b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1405b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1406b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1407b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1408b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1409b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1410b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1411b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1412b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1413b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1414b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1415a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1416a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1417a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1418b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1419b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1420b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1421b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1422b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1423b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1424b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1425b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1426b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1427b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1428b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1429b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1430b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1431bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1432bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1433bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1434b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1435b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1436b22d9ee0a364b10d488dd6a2e8ba69d5ca7f6258Jinsuk Kim            enforceAccessPermission();
1437b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1438b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1439b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1440b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1441b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1442b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1443b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1444b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1445b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1446b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1447a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1448f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1449f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1450b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void sendMhlVendorCommand(final int portId, final int offset, final int length,
1451f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                final byte[] data) {
1452f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1453f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            runOnServiceThread(new Runnable() {
1454f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                @Override
1455f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                public void run() {
1456f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (!isControlEnabled()) {
1457f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Hdmi control is disabled.");
1458f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return ;
1459f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
14603b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim                    HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
1461f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    if (device == null) {
1462f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        Slog.w(TAG, "Invalid port id:" + portId);
1463f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                        return;
1464f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    }
1465b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    mMhlController.sendVendorCommand(portId, offset, length, data);
1466f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1467f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            });
1468f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1469f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1470f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        @Override
1471b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        public void addHdmiMhlVendorCommandListener(
1472b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                IHdmiMhlVendorCommandListener listener) {
1473f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            enforceAccessPermission();
1474b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            HdmiControlService.this.addHdmiMhlVendorCommandListener(listener);
1475f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1476959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1477959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        @Override
1478959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
1479959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1480959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
1481959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo
1482959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
1483959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mProhibitMode: " + mProhibitMode);
1484959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            if (mCecController != null) {
1485959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("mCecController: ");
1486959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.increaseIndent();
1487959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                mCecController.dump(pw);
1488959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.decreaseIndent();
1489959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1490959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPortInfo: ");
1491959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.increaseIndent();
1492959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            for (HdmiPortInfo hdmiPortInfo : mPortInfo) {
1493959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo                pw.println("- " + hdmiPortInfo);
1494959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            }
1495959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.decreaseIndent();
1496959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo            pw.println("mPowerStatus: " + mPowerStatus);
1497959d2db12c7c6a06465af1251bc4cece580a72a3Terry Heo        }
149878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
149978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1500a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
150179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
150279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
150379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
15047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
15057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1506c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
15077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
15087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
150979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
151078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
151178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1512a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
151379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
151479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
151579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
15167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
15177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1518c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
15197fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
15207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
152179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
152278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
152378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
152478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
152578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
152678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
152778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
152878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
152978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
153078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
153178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
153278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
153378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
153478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
153578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
153678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
153778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
153878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
153978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
154078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
154178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
154278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
154378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
154478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
154578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
154678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
154778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
15487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
15496d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
15504893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
15514893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
15524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
15534893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
15544893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
15554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
15564893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
15576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
15584893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
15594893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
15604893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
15614893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
156261daf6b38e7a7ada2a6ca5a60539a54b9c6810bdJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
15634893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
1564f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
15654893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
1566f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onStatusChanged(device, status);
15674893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
15684893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
15696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
15706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
15716d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
15726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
15736d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1574ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1575ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1576ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1577ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1578ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1579ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1580ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1581ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1582ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1583ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1584ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1585ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1586ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1587ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1588ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1589ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1590ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1591ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1592ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1593ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1594ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1595ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1596ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1597ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1598ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1599ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1600ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
16019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
1602f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        private final IHdmiInputChangeListener mListener;
1603f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1604f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        public InputChangeListenerRecord(IHdmiInputChangeListener listener) {
1605f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mListener = listener;
1606f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1607f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
16089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
16099c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
16109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
1611f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                mInputChangeListenerRecord = null;
16129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16139c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16149c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16159c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
16169c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
16179c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1618f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mInputChangeListenerRecord = new InputChangeListenerRecord(listener);
16199c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
16209c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
16219c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
16229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
16239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
16249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
162861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
16299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
1630f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mInputChangeListenerRecord != null) {
16319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
1632f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    mInputChangeListenerRecord.mListener.onChanged(info);
16339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
16349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
16359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
16369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
16379c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
16389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
16399c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
164012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1641b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1642f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord(listener);
1643b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
164412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1645b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
164612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1647b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1648b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1649b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1650b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1651b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1652b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
1653f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
165412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1655f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    return mRecordListenerRecord.mListener.getOneTouchRecordSource(recorderAddress);
165612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
165712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1658b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1659b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1660b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1661b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1662b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1663b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1664326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeOneTouchRecordResult(int recorderAddress, int result) {
166512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1666f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
166712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1668326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onOneTouchRecordResult(recorderAddress, result);
166912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
167012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
167112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
167212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
167312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
167412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
167512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
1676326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeTimerRecordingResult(int recorderAddress, int result) {
167712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
1678f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
167912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
1680326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onTimerRecordingResult(recorderAddress, result);
168112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1682e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1683e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1684e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1685e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1686e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1687e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1688326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang    void invokeClearTimerRecordingResult(int recorderAddress, int result) {
1689e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1690f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            if (mRecordListenerRecord != null) {
1691e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1692326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                    mRecordListenerRecord.mListener.onClearTimerRecordingResult(recorderAddress,
1693326aef0c9402742e29c4503c857f93e75cf9a6ecJungshik Jang                            result);
1694e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1695e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
169612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
169712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
169812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
169912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
170012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
17017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
17027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
17037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
17047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
17057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
17067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
17077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
170863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1709f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    private void invokeSystemAudioModeChangeLocked(IHdmiSystemAudioModeChangeListener listener,
1710ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1711ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1712ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1713ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1714ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1715ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1716ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1717ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
17184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
17194893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
172060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
1721f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
1722f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                invokeHotplugEventListenerLocked(record.mListener, event);
172360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
172460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
172560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
172660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
17274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
172860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
172960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
173060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
173160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
173260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
173360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1734e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1735e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
173679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
173761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
173879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
173979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1740e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1741b8d62e70bf2612e9c75bf7d8b2370f80e5877f95Yuncheol Heo        return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV);
1742e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1743e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
174479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1745c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
174661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
174760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1748a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1749a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1750a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1751a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
175292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
175392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
175492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
175592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
175692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
175792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
175838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1759f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
176038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
1761f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
176238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
176338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
176438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1765f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
176638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1767f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1768c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1769c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
177038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
177138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1772f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
177338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1774f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1775c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1776c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
177738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
177838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1779f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
178038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1781f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1782c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
178338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
178438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
178538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
178638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
178738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1788fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
178938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
179038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
179138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
179238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
179338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
179438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
179538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
179638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
179738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
179838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
179938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
1800c12035cd40d01b032013f515cb509e6c8791cf65Jeff Brown        pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
180138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
180238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
180338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
180438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
180538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
180638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
180738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1808c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
180938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1810a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1811fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1812fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1813fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1814fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1815fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1816a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
181738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
181838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
181938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
182038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
182138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
182238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
182338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
182438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
182538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1826c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
18270608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        invokeVendorCommandListenersOnControlStateChanged(false,
18280608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
18294fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
18314fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
18324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
18334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
18344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
18354fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
18364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
18374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
18384b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
18394b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
18404b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
18414fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
18424fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
18434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
18444fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
18454fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18461ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    @ServiceThreadOnly
18471ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    private void onLanguageChanged(String language) {
18481ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        assertRunOnServiceThread();
18491ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        mLanguage = language;
18501ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
18511ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        if (isTvDevice()) {
18521ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo            tv().broadcastMenuLanguage(language);
18531ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo        }
18541ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo    }
18551ca0a43251a31bb1b4253dc404316cc4b840f497Terry Heo
1856f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    @ServiceThreadOnly
1857f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    String getLanguage() {
1858f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        assertRunOnServiceThread();
1859f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang        return mLanguage;
1860f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang    }
1861f67113f7abd536cc3eb888344bf925762aa5278eJungshik Jang
18624fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
1863350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang        if (mCecController != null) {
1864350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
1865350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang                device.disableDevice(mStandbyMessageReceived, callback);
1866350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang            }
186738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1868350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang
1869f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.clearAllLocalDevices();
187038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
187138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
187238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
18734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
18744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
18754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
18764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
18774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
18784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
18794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
18804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
18814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
18824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
18834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
188438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
18854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
18864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1887c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
188838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
188938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1890c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
189138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
18924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
189338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
189438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
18955008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
189638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
18974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1898119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1899119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1900119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1901119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1902119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1903119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1904119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1905119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1906119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1907119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1908119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1909119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1910119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
19110608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress,
19120608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            byte[] params, boolean hasVendorId) {
1913119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1914d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            if (mVendorCommandListenerRecords.isEmpty()) {
1915d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim                return false;
1916d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            }
1917119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1918119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1919119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1920119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1921119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
19220608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
1923119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1924119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1925119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1926119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1927d4a94db1cd44a536d535de890a0a14919a39a0dcJinsuk Kim            return true;
1928119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1929119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1930119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
19310608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) {
19320608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        synchronized (mLock) {
19330608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            if (mVendorCommandListenerRecords.isEmpty()) {
19340608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                return false;
19350608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
19360608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
19370608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                try {
19380608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    record.mListener.onControlStateChanged(enabled, reason);
19390608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                } catch (RemoteException e) {
19400608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e);
19410608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                }
19420608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            }
19430608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            return true;
19440608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
19450608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo    }
19460608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo
1947b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
1948b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim        HdmiMhlVendorCommandListenerRecord record =
1949b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                new HdmiMhlVendorCommandListenerRecord(listener);
1950f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        try {
1951f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            listener.asBinder().linkToDeath(record, 0);
1952f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        } catch (RemoteException e) {
1953f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            Slog.w(TAG, "Listener already died.");
1954f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            return;
1955f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1956f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1957f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1958b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            mMhlVendorCommandListenerRecords.add(record);
1959f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1960f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
1961f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
1962b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim    void invokeMhlVendorCommandListeners(int portId, int offest, int length, byte[] data) {
1963f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        synchronized (mLock) {
1964b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim            for (HdmiMhlVendorCommandListenerRecord record : mMhlVendorCommandListenerRecords) {
1965f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                try {
1966f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                    record.mListener.onReceived(portId, offest, length, data);
1967f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                } catch (RemoteException e) {
1968b3fbf9dbe8d41d91efbac2118b676af74592257bJinsuk Kim                    Slog.e(TAG, "Failed to notify MHL vendor command", e);
1969f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang                }
1970f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang            }
1971f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang        }
1972f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang    }
1973f424932cfb1b16b01a37500d09e295912700a51dJungshik Jang
19744d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
19754d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
19764d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
19774d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
19784d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
19794d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
19804d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
19814d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
19824d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
19834d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
19844d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
19854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
1987350e68d0b80c22e6ec37dd683134f46079619803Jungshik Jang    void setCecOption(int key, int value) {
19885008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        assertRunOnServiceThread();
19895008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(key, value);
19905008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    }
19915008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim
19925008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    @ServiceThreadOnly
19935008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim    void setControlEnabled(boolean enabled) {
19944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
19954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
19960608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        if (!enabled) {
19970608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            // Call the vendor handler before the service is disabled.
19980608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo            invokeVendorCommandListenersOnControlStateChanged(false,
19990608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo                    HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
20000608b9328b1c2f804ffb2d4165c34383d34bde2aYuncheol Heo        }
20015008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        int value = toInt(enabled);
20025008486b09c588bf3409b70d9ee29225e8593c64Jinsuk Kim        mCecController.setOption(OPTION_CEC_ENABLE, value);
2003f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        mMhlController.setOption(OPTION_MHL_ENABLE, value);
20044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
20064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
20074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
20084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
20094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
2010fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
20114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
20124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
20134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
20144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
20154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
20164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
20174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
20184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
20194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
20204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
2021867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang
2022867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    @ServiceThreadOnly
2023867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    void setActivePortId(int portId) {
2024867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        assertRunOnServiceThread();
2025867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang        mActivePortId = portId;
2026e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2027e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // Resets last input for MHL, which stays valid only after the MHL device was selected,
2028e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // and no further switching is done.
2029e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        setLastInputForMhl(Constants.INVALID_PORT_ID);
2030e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2031e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2032e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2033e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void setLastInputForMhl(int portId) {
2034e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2035e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        mLastInputMhl = portId;
2036e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2037e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2038e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2039e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    int getLastInputForMhl() {
2040e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2041e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        return mLastInputMhl;
2042e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    }
2043e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2044e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    /**
2045e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * Performs input change, routing control for MHL device.
2046e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     *
2047e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param portId MHL port, or the last port to go back to if {@code contentOn} is false
2048e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     * @param contentOn {@code true} if RAP data is content on; otherwise false
2049e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim     */
2050e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    @ServiceThreadOnly
2051e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim    void changeInputForMhl(int portId, boolean contentOn) {
2052e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        assertRunOnServiceThread();
2053e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        final int lastInput = contentOn ? tv().getActivePortId() : Constants.INVALID_PORT_ID;
2054e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().doManualPortSwitching(portId, new IHdmiControlCallback.Stub() {
2055e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            @Override
2056e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            public void onComplete(int result) throws RemoteException {
2057e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // Keep the last input to switch back later when RAP[ContentOff] is received.
2058e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // This effectively sets the port to invalid one if the switching is for
2059e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                // RAP[ContentOff].
2060e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim                setLastInputForMhl(lastInput);
2061e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim            }
2062e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        });
2063e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2064e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // MHL device is always directly connected to the port. Update the active port ID to avoid
2065e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // unnecessary post-routing control task.
2066e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        tv().setActivePortId(portId);
2067e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim
2068e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // The port is either the MHL-enabled port where the mobile device is connected, or
2069f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim        // the last port to go back to when turnoff command is received. Note that the last port
2070e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // may not be the MHL-enabled one. In this case the device info to be passed to
2071e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        // input change listener should be the one describing the corresponding HDMI port.
20723b9309a01c9aa0544f97b2ec6abe7b254d829336Jinsuk Kim        HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
207393eed0c03a1f9cd66946760045122516483ba3f0Jinsuk Kim        HdmiDeviceInfo info = (device != null) ? device.getInfo() : mPortDeviceMap.get(portId);
2074e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim        invokeInputChangeListener(info);
2075867b4e0c55b4b1e432a3585fc945a999f066ef81Jungshik Jang    }
207608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
2077e9f6ed3b11fb8ebae5e73db1e4736b86cae272d9Jinsuk Kim   void setMhlInputChangeEnabled(boolean enabled) {
2078f286b4d86b4b2ac91edb88d0336810e46d9a16ceJinsuk Kim       mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
207908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
208008a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
208108a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            mMhlInputChangeEnabled = enabled;
208208a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
208308a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
208408a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo
208508a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    boolean isMhlInputChangeEnabled() {
208608a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        synchronized (mLock) {
208708a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo            return mMhlInputChangeEnabled;
208808a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo        }
208908a1be81d7b597f858164fee6a4934264259b3aeYuncheol Heo    }
2090339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang
2091339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    @ServiceThreadOnly
2092339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    void displayOsd(int messageId) {
2093339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        assertRunOnServiceThread();
2094339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
2095339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
2096339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
2097339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang                HdmiControlService.PERMISSION);
2098339227da7cf025ce4ae0c85ddc52643d63972321Jungshik Jang    }
20992e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang
21002e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    @ServiceThreadOnly
21012e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    void displayOsd(int messageId, int extra) {
21022e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        assertRunOnServiceThread();
21032e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
21042e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
21052b0da5c4c84305f1d391dc78b85e244c9fd92456Yuncheol Heo        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
21062e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
21072e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang                HdmiControlService.PERMISSION);
21082e8f1b6399089626b4f0249427626ba6e63a62efJungshik Jang    }
21090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
2110