HdmiControlService.java revision 410ca9c7a4a2d69af5c81e76320433bfda05cafe
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
190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.annotation.Nullable;
2038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.BroadcastReceiver;
217ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.content.ContentResolver;
220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
2338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
2438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
2561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
26c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
2760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
29c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiTvClient;
30d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
31d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
326d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
33d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
3512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jangimport android.hardware.hdmi.IHdmiRecordListener;
36ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
37119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
38a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
3942c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
4067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
4278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
43e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
4438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
4578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
4638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
477ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kimimport android.provider.Settings.Global;
482b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kimimport android.util.ArraySet;
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
518b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
5278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
534893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
55a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
577e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kimimport com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
584fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
60b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
61b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
6278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
63f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
640340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
6502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
66a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
680792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
690792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
700792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
74c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
7578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
76fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    // The reason code to initiate intializeCec().
77fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_ENABLE_CEC = 0;
78fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_BOOT_UP = 1;
79fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_SCREEN_ON = 2;
80fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    static final int INITIATED_BY_WAKE_UP_MESSAGE = 3;
81fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
82d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
83d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
84d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
85d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
86d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
87d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
88d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
89ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
934fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
95d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
96d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
97d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
98d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
9902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
10002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
10102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
10202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
10302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
10402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
10502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
10602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
10702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
10802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
10902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
11002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
11138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
11238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
11338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
11438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
11538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
11638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
11738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
11838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
11938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
12038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
12138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
12238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
12338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
12438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
12538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
12638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
12738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
12838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1310792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
13478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
13578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
13678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
13978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
14078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
14178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1424893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
14378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
14478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
14578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
14778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
14878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
14978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1564893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
160119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    // List of records for vendor command listener to handle the the caller killed in action.
161119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
162119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
163119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
164119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1689c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1699c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1709c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
171b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
17212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private IHdmiRecordListener mRecordListener;
173b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
174b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
17512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
176b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
17792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
17892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
17992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
18092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
18192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1824d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1834d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1844d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1854d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
1864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
1874d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
188ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
189ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
190ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
191ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
192ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
193ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
194ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
195ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1964893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
2010792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2020792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
2030792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
2040792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
2060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2092b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
21030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseIntArray mPortIdMap;
2112b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2122b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
21330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
2142b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
21575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
21675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
21738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
21838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
21938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
220c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
22138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
22238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
22338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
22438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
225fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    @ServiceThreadOnly
226fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private boolean mWakeUpMessageReceived = false;
227fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
2280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2290792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
2320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2330792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2340792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2350792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2362f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
237c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
2387ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
2397ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
2408b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
241a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
2423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
243347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
244347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
245347a60449981fc934e5a84122df87c1447665548Yuncheol Heo                    HdmiTvClient.ENABLED);
246347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
247a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
248a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
249fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(INITIATED_BY_BOOT_UP);
250a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
251a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
255e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2570792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2592b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        initPortInfo();
26075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
2618692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
26263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
26338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
26438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
26538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
26638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
26738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
26838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
26938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
2707ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
27138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
27225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    /**
27325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     * Called when the initialization of local devices is complete.
27425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo     */
27525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    private void onInitializeCecComplete() {
276fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
277fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
278fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        }
279fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = false;
280fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo
28125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        if (isTvDevice()) {
28225c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo            mCecController.setOption(HdmiTvClient.OPTION_CEC_AUTO_WAKEUP,
28325c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo                    tv().getAutoWakeup() ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED);
28425c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo        }
28525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo    }
28625c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo
2877ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
2887ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
2897ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        return Global.getInt(cr, key, defVal ? Constants.TRUE : Constants.FALSE) == Constants.TRUE;
2907ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
2917ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
2927ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
2937ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
2947ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        Global.putInt(cr, key, value ? Constants.TRUE : Constants.FALSE);
2950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
296e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
297fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeCec(int initiatedBy) {
298a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
299a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang                HdmiTvClient.ENABLED);
300fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        initializeLocalDevices(mLocalDevices, initiatedBy);
301a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
302a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
303a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
304fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void initializeLocalDevices(final List<Integer> deviceTypes, final int initiatedBy) {
305a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
3073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
308fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        final int[] finished = new int[1];
3094b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo        clearLocalDevices();
3103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
3113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
3123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
3133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
3143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
3153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
3163ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
317c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
3183ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
3193ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
320410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // Set POWER_STATUS_ON to all local devices because they share lifetime
321410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        // with system.
322410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                        HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
323410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                                HdmiControlManager.POWER_STATUS_ON);
3243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
3253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
3263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
3273ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
3283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3304893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
331fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    if (deviceTypes.size() == ++finished[0]) {
332fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        onInitializeCecComplete();
333fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        notifyAddressAllocated(devices, initiatedBy);
3343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
3363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
3373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
340a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
341fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
342a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3433ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
3443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
3453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
346fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
3473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
3510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
352a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3532b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
354a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
3560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
3580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
3590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
3600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
3610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
3632b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
3642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3652b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
36630c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
36730c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        SparseIntArray portIdMap = new SparseIntArray();
3682b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
36930c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portIdMap.put(info.getAddress(), info.getId());
37030c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim            portInfoMap.put(info.getId(), info);
3710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
37230c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
37330c74d9ba60a7208a587ecd6dcdfef1cfbd7fce7Jinsuk Kim        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
3740340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
375f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        if (mMhlController == null) {
376f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
377f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            return;
378f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        } else {
379f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
380f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
381f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : mhlPortInfo) {
382f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (info.isMhlSupported()) {
383f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    mhlSupportedPorts.add(info.getId());
384f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3850340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3860340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
387f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            // Build HDMI port info list with CEC port info plus MHL supported flag.
388f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
389f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : cecPortInfo) {
390f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (mhlSupportedPorts.contains(info.getId())) {
391f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
392f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                            info.isCecSupported(), true, info.isArcSupported()));
393f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                } else {
394f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(info);
395f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3962b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
397f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(result);
3982b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
4000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
4010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
4020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
4030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
4040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
4050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
4060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
4070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
4082b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
4090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
4100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
411e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
412401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
413401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
414401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
415401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
416401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
417401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
418401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
419c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
420401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
421401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
422401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
423401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
424401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
425401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
426401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
427401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
428401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
429401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
430401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
431c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
4322b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
433401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
434401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
43509ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
4362b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
43709ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
43809ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
439401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
440e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
441e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
442e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
443e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
444e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
445e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
446e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
447e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
448e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
449e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
450e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
451e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
452e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
453e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
454e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
45567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
456e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
457c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
458c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
4593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
4603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
4623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
4633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
4663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
4673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
4693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
4703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
472a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
47361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
4740340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
47579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
47679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
47779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
47879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
47979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
480a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
481a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
4823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
483092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
484092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
485092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
486092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
487092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
488092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
489092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
49060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
49160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
49260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
4932b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        int portId = mPortIdMap.get(physicalAddress);
4942b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
4952b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
49660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
49760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
49860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
49960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
50079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
50167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
50267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
50367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
50463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
50563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
50663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
50763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
50863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
50963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
51063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
51163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
51263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
51363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
51467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
515c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
516c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
517c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
518d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
519c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
520a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
521d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
522a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5235f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        if (mMessageValidator.isValid(command)) {
5245f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            mCecController.sendCommand(command, callback);
5255f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        } else {
5265f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            Slog.e(TAG, "Invalid message type:" + command);
5275f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            if (callback != null) {
5285f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang                callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
5295f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang            }
5305f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        }
531d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
532d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
533a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
534d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
535a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5365f75cbd8593e83eaf17cfac07186a3b6a7b7b1f1Jungshik Jang        sendCecCommand(command, null);
537c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
538c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
5396aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
5406aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
5416aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
5426aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
5436aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
5446aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
5456aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
5466aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
5476aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
5486aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
5496aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
5506aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
551a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
552a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
553a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
55475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
55575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
55675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
557092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
558092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
559092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
56079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
56179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
56260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
56360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
564a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
565092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
566a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
567092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
56879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
569c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
570092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
571092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
572092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
57360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
574c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
5753a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
5763a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
577092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
578a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
579a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
58067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
58167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
58267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
5838b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
58467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
58567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
586a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
58767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
58860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
58979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
59079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
59160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
59260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
59367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
59467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
59502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
59602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
59702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
59802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
59902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
6001de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
6010f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
60202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
6030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
60402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
605a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
6061de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
6071de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
608a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6091de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
6101de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
6110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
6120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
6130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
614c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
6150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
6160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
6170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
618c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
6190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
6200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
6210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
6220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
62302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
62402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
62560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
62660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
62760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
62860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
62960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
63079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
63179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
63279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
63379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
63479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
635b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
636b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
637b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
638b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
639b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
640b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
641b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
642b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
643b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
644b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
645b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
646b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
647b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
648b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
649b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
650b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
6513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
653ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
654ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
655ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
656ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
657ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
658ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
659410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) {
66042c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
66142c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
66261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return new HdmiDeviceInfo(logicalAddress,
6632b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
6642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
6653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
66778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
66878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
66978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
67078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
67178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
67278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
67378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
67478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
67578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
67678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
67778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
67878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
67978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
68078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
68178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
68278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
68378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
68478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
6856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
6866d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
6876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
6896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
6906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
693ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
6946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
6956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
6966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
6976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
6986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
7006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
701ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
70238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
703ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
704ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
705ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
706ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
707ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
708ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
709ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
710ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
711ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
712ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
713ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
714ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
715ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
716ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
717119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
718119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
719119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
720119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
721119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
722119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
723119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
724119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
725119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
726119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
727119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
728119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
729119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
730119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
731119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
732119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
733119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
73412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
735b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
736b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
737b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
73812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                mRecordListener = null;
739b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
740b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
741b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
742b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
74378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
74478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
74578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
74678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
74778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
74878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
74978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
75078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
7520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
7530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
7540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
75578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
7560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
75778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
75978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
76061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public HdmiDeviceInfo getActiveSource() {
7617e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
7627e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
7637e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
7647e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
7657e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7667e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
7677e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
76861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activeSource.logicalAddress,
76961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        activeSource.physicalAddress, HdmiDeviceInfo.PORT_INVALID,
77061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                        HdmiDeviceInfo.DEVICE_INACTIVE, 0, "");
7717e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7727e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
77361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            if (activePath != HdmiDeviceInfo.PATH_INVALID) {
77461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                return new HdmiDeviceInfo(activePath, tv.getActivePortId());
7757e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7767e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
7777e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
7787e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
7797e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
780a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
781a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
782a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
783a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
784a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
78572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
78672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
78772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
78872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
78979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
790a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
791a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
792c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
793a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
794a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
795a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
796a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
797a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
798a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
799a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
800a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
801a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
802a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
803a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
804a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
805a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
80672b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
80772b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
80872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
80972b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
810a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
811a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
812a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
813c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
814a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
815a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
8168333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
817a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
818a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
819a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
820a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
821a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
822c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
823a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
824a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
825a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
826a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
827c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
828c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    if (localDevice == null) {
829c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                        Slog.w(TAG, "Local device not available");
830a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
831a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
832c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    localDevice.sendKeyEvent(keyCode, isPressed);
833a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
834a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
835a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
836a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
837a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
8387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
83978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
8447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
84678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
84778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
84878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
85078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8537fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8547fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
8557fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
85778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
85878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
85978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
86178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
8667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
86878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
86978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
87078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
87278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
8777fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
87978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
8806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8826d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
8836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8846d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
8856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
886ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
8876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
8886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
8896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
8906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
8916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
8946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
8966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
897ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
898ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
899ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
900ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
901ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
902ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
903ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
904ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
905e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
906ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
907ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
908ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
909ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
910ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
911ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
912ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
913ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
914ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
915377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
916ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
917ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
918ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
919ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
920ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
921ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
922ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
923ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
924ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
925ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
926ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
927c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
928ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
929ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
930ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
931ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
932ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
933ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
934ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
935ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
936ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
937ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
938ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
939ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
940ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
941ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
942ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
943ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
944ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
945ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
946ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
947ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
94892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
94992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
9509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
9519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
9529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
9539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
9549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
9559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
95661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        public List<HdmiDeviceInfo> getInputDevices() {
9579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
9589c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
9599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
9609c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
9619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
9629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
9639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
9649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
9659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
9669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
9679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
968160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setControlEnabled(final boolean enabled) {
96992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            enforceAccessPermission();
97092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            runOnServiceThread(new Runnable() {
97192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                @Override
97292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                public void run() {
9734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleHdmiControlStatusChanged(enabled);
9744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
97592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                }
97692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            });
97792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
978a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang
979a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        @Override
98041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
98141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
98241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
98341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
98441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
98541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
98641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
98741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
98841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
98941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
99041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
99141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
99241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
99341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
99441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
99541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
99641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
99741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
99841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
99941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
100041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
100141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
100241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
100341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
100441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
100541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
100641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
100741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
100841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
100941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
101041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
101141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
101241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1013a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1014a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1015a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1016a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1017a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1018a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1019a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
102038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1021a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1022a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1023a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1024a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1025a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1026160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1027160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
1028160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setOption(final int key, final int value) {
10294d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
1030160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            if (!isTvDevice()) {
1031160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                return;
1032160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
1033160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            switch (key) {
1034c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
103525c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo                    tv().setAutoWakeup(value == HdmiTvClient.ENABLED);
1036160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(key, value);
1037160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1038c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF:
1039160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    // No need to pass this option to HAL.
1040c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED);
1041160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1042c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING:  // Fall through
1043c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_POWER_CHARGE:
1044160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
1045160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(key, value);
1046160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
1047160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1048160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
1049160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
1050160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
10514d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        @Override
10524d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
10534d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
10544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
10554d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
10564d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
10574d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
10584d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1059119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1060119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1061119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1062119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1063119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1064119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1065119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1066119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1067119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1068119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1069119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
1070119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1071119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1072119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1073119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1074119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1075119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1076119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1077119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1078119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1079119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1080119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1081119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1082119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1083119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1084119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1085119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1086119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1087119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1088119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1089119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1090119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1091119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1092119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1093119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
109412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1095a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1096a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
109712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
109812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1099b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1100b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1101b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1102b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1103b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1104b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1105b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1106b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1107b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1108b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1109b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1110b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1111b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1112b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1113b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1114b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1115b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1116b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1117b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1118b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1119b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1120b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1121b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1122b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1123b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1124b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1125b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1126b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1127a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1128a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1129a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1130b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1131b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1132b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1133b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1134b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1135b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1136b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1137b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1138b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1139b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1140b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1141b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1142bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1143bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1144bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1145b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1146b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1147b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1148b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1149b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1150b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1151b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1152b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1153b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1154b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1155b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1156b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1157a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
115878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
115978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1160a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
116179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
116279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
116379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
11647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
11657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1166c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11687fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
116979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
117078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
117178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1172a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
117379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
117479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
117579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
11767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
11777fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1178c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
118179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
118278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
118378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
118478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
118578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
118678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
118778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
118878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
118978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
119078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
119178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
119278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
119378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
119478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
119578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
119678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
119778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
119878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
119978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
120078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
120178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
120278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
120378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
120478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
120578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
120678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
120778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
120878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
120978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
12107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
12116d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
12124893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
12134893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
12144893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
12154893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
12164893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
12174893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
12184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
12196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
12204893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
12214893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
12224893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
12234893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
12244893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
122561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeDeviceEventListeners(HdmiDeviceInfo device, boolean activated) {
12264893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
12274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
12284893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
12294893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
12304893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
12314893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
12326d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
12336d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
12346d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
12356d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
12366d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1237ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1238ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1239ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1240ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1241ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1242ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1243ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1244ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1245ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1246ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1247ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1248ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1249ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1250ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1251ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1252ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1253ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1254ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1255ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1256ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1257ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1258ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1259ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1260ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1261ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1262ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1263ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1264ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1265ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
12669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
12679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
12689c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
12699c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
12709c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
12719c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12729c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12739c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12749c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12759c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
12769c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12779c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
12789c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
12799c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
12809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
12819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
12829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
12839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12849c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
12859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12879c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
128861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang    void invokeInputChangeListener(HdmiDeviceInfo info) {
12899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12909c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
12919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
129272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    mInputChangeListener.onChanged(info);
12939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
12949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
12959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
12969c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12979c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12999c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
130012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1301b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
130212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord();
1303b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
130412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1305b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
130612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1307b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
130812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListener = listener;
1309b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1310b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1311b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1312b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1313b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
131412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
131512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
131612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    return mRecordListener.getOneTouchRecordSource(recorderAddress);
131712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
131812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1319b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1320b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1321b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1322b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1323b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1324b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
132512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
132612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
132712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
132812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
132912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onOneTouchRecordResult(result);
133012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
133112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
133212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
133312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
133412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
133512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
133612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
133712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
133812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
133912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
134012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
134112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onTimerRecordingResult(result);
134212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1343e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1344e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1345e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1346e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1347e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1348e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1349e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    void invokeClearTimerRecordingResult(int result) {
1350e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1351e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            if (mRecordListener != null) {
1352e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1353e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    mRecordListener.onClearTimerRecordingResult(result);
1354e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1355e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
135612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
135712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
135812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
135912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
136012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
13617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
13627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
13637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
13647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
13657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
13667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
13677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
136863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1369ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1370ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1371ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1372ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1373ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1374ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1375ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1376ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1377ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
13784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
13794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
138060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
138160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
13824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
138360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
138460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
138560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
138660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
13874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
138860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
138960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
139060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
139160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
139260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
139360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1394e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1395e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
139679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
139761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
139879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
139979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1400e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1401e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1402e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1403e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
140479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1405c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
140661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang                mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
140760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1408a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1409a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1410a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1411a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
141292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
141392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
141492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
141592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
141692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
141792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
141838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
141938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
142038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
142138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
142238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
142338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1424c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1425c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
142638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
142738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
142838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1429c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1430c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
143138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
143238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
143338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1434c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
143538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
143638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
143738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
143838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
143938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1440fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
144138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
144238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
144338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
144438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
144538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
144638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
144738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
144838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
144938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
145038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
145138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
145238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
145338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
145438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
145538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
145638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
14572849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    void nap() {
14582849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
14592849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo        pm.nap(SystemClock.uptimeMillis());
14602849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo    }
14612849b7ccd1cf973f862dd9b95355afbc9ca9268bYuncheol Heo
146238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
146338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
146438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1465c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
146638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1467a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1468fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1469fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1470fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1471fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1472fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1473a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
147438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
147538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
147638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
147738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
147838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
147938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
148038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
148138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
148238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1483c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
14844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
14864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
14874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
14884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
14894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
14904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
14914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
14924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
14934b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
14944b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
14954b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
14964fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
14974fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
14984fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
14994fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
15004fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
150238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
15034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.disableDevice(mStandbyMessageReceived, callback);
150438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
150538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
150638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
150738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
15084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
15094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
15104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
15114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
15124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
15144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
15154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
15164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
15184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
151938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
15204fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
15214fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1522c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
152338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
152438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1525c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
152638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
15274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
152838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
152938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
1530c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
153138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
15324d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1533119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1534119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1535119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1536119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1537119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1538119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1539119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1540119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1541119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1542119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1543119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1544119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1545119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1546119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1547119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1548119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1549119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1550119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1551119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1552119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1553119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1554119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1555119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1556119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1557119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1558119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1559119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1560119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1561119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
15624d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
15634d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
15644d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
15654d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
15664d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
15674d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
15684d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
15694d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
15704d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
15714d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
15724d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
15734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
15754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleHdmiControlStatusChanged(boolean enabled) {
15764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
15774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED;
15794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value);
15804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mMhlController != null) {
15814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value);
15824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
15854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
15864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1589fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
15904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
15914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
15924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
15934fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
15944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
15954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
15964fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
15974fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
15984fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15994fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
16000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1601