HdmiControlService.java revision 6aae6528a6672497b1d1dffb5c083093d5c46dc8
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;
25c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
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.
21026ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo    private final SparseIntArray mPortIdMap = new SparseIntArray();
2112b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2122b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
21326ba7fddcaeb052710ca8672889830dabcbfd3acYuncheol Heo    private final SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
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 {
3203ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
3213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
3223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
3233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
3243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
3253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3263ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
328fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    if (deviceTypes.size() == ++finished[0]) {
329fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        onInitializeCecComplete();
330fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                        notifyAddressAllocated(devices, initiatedBy);
3313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
3333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
3343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
337a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
338fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices, int initiatedBy) {
339a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
3413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
3423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
343fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            device.handleAddressAllocated(address, initiatedBy);
3443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
3480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
349a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3502b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
351a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
3530340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
3550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
3560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
3570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
3580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
3602b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
3612b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3622b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
3632b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
3642b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            mPortIdMap.put(info.getAddress(), info.getId());
3652b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            mPortInfoMap.put(info.getId(), info);
3660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
368f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        if (mMhlController == null) {
369f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
370f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            return;
371f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        } else {
372f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
373f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
374f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : mhlPortInfo) {
375f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (info.isMhlSupported()) {
376f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    mhlSupportedPorts.add(info.getId());
377f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3780340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3790340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
380f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            // Build HDMI port info list with CEC port info plus MHL supported flag.
381f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
382f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : cecPortInfo) {
383f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (mhlSupportedPorts.contains(info.getId())) {
384f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
385f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                            info.isCecSupported(), true, info.isArcSupported()));
386f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                } else {
387f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(info);
388f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3892b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
390f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(result);
3912b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
3950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
3960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
3970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
3980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
3990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
4002b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
4010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
4022b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
4032b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
4040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
4050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
406e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
407401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
408401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
409401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
4102b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
411401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
4122b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
413401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
414401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
415401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
416c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
417401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
418401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
419401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
420401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
421401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
422401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
423401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
424401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
425401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
426401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
4272b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
428401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
4292b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
430c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
4312b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
432401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
433401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
4342b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
43509ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
4362b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
4372b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
43809ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
43909ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
440401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
441e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
442e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
443e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
444e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
445e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
446e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
447e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
448e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
449e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
450e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
451e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
452e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
453e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
454e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
455e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
45667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
457e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
458c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
459c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
4603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
4613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
4633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
4643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
4673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
4683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
4703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
4713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
473a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
474a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
4750340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
47679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
47779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
47879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
47979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
48079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
481a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
482a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
4833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
484092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
485092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
486092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
487092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
488092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
489092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
490092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
49160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
49260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
4932b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
49460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
4952b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
4962b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        int portId = mPortIdMap.get(physicalAddress);
4972b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
4982b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
49960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
50060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
50160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
50260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
50379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
50467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
50567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
50667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
50763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
50863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
50963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
51063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
51163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
51263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
51363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
51463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
51563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
51663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
51767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
518c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
519c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
520c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
521d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
522c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
523a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
524d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
525a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
526d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
527d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
528d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
529a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
530d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
531a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
532d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
533c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
534c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
5356aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    /**
5366aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * Send <Feature Abort> command on the given CEC message if possible.
5376aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * If the aborted message is invalid, then it wont send the message.
5386aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param command original command to be aborted
5396aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     * @param reason reason of feature abort
5406aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo     */
5416aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    @ServiceThreadOnly
5426aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
5436aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        assertRunOnServiceThread();
5446aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo        mCecController.maySendFeatureAbortCommand(command, reason);
5456aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo    }
5466aae6528a6672497b1d1dffb5c083093d5c46dc8Yuncheol Heo
547a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
548a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
549a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
55075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
55175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
55275a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
553092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
554092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
555092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
55679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
55779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
55860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
55960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
560a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
561092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
562a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
563092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
56479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
565c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
566092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
567092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
568092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
56960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
570c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
5713a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
5723a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
573092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
574a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
575a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
57667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
57767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
57867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
5798b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
58067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
58167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
582a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
58367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
58460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
58579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
58679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
58760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
58860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
58967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
59067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
59102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
59202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
59302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
59402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
59502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
5961de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
5970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
59802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
5990f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
60002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
601a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
6021de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
6031de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
604a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
6051de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
6061de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
6070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
6080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
6090f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
610c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
6110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
6120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
6130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
614c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
6150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
6160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
6170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
6180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
61902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
62002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
62160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
62260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
62360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
62460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
62560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
62679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
62779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
62879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
62979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
63079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
631b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
632b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
633b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
634b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
635b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
636b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
637b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
638b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
639b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
640b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
641b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
642b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
643b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
644b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
645b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
646b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
6473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
649ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
650ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
651ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
652ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
653ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
654ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
6553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
65642c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
65742c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
6583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
6592b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
6602b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
6613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
66378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
66478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
66578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
66678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
66778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
66878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
66978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
67078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
67178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
67278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
67378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
67478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
67578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
67678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
67778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
67878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
67978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
68078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
6816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
6826d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
6836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6846d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
6856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
6866d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
689ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
6906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
6916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
6926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
6936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
6946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
6966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
697ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
69838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
699ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
700ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
701ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
702ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
703ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
704ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
705ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
706ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
707ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
708ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
709ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
710ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
711ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
712ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
713119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
714119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
715119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
716119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
717119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
718119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
719119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
720119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
721119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
722119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
723119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
724119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
725119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
726119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
727119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
728119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
729119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
73012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
731b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
732b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
733b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
73412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                mRecordListener = null;
735b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
736b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
737b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
738b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
73978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
74078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
74178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
74278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
74378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
74478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
74578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
74678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
7480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
7490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
7500340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
75178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
7520340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
75378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
75478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
75578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7567e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        public HdmiCecDeviceInfo getActiveSource() {
7577e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
7587e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (tv == null) {
7597e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                Slog.w(TAG, "Local tv device not available");
7607e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return null;
7617e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7627e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            ActiveSource activeSource = tv.getActiveSource();
7637e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activeSource.isValid()) {
7647e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return new HdmiCecDeviceInfo(activeSource.logicalAddress,
7657e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                        activeSource.physicalAddress, HdmiCecDeviceInfo.PORT_INVALID,
7667e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                        HdmiCecDeviceInfo.DEVICE_INACTIVE, 0, "");
7677e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7687e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            int activePath = tv.getActivePath();
7697e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            if (activePath != HdmiCecDeviceInfo.PATH_INVALID) {
7707e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim                return new HdmiCecDeviceInfo(activePath, tv.getActivePortId());
7717e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            }
7727e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim            return null;
7737e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        }
7747e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim
7757e74206693f4ee93afb902d5b3446e2384f2a13dJinsuk Kim        @Override
776a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
777a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
778a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
779a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
780a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
78172b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
78272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
78372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
78472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
78579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
786a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
787a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
788c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
789a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
790a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
791a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
792a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
793a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
794a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
795a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
796a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
797a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
798a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
799a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
800a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
801a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
80272b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    if (callback == null) {
80372b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        Slog.e(TAG, "Callback cannot be null");
80472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                        return;
80572b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    }
806a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
807a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
808a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
809c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
810a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
811a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
8128333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
813a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
814a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
815a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
816a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
817a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
818c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
819a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
820a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
821a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
822a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
823c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
824c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    if (localDevice == null) {
825c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                        Slog.w(TAG, "Local device not available");
826a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
827a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
828c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    localDevice.sendKeyEvent(keyCode, isPressed);
829a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
830a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
831a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
832a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
833a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
8347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
83578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
8407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
84278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
84378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
84478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
84678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
8517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
85378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
85478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
85578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
85778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8587fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
8627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
86478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
86578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
86678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
86878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8697fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8707fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
8737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8747fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
87578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
8766d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8776d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8786d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
8796d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
8816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
882ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
8836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
8846d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
8856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
8866d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
8876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
8906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
8926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
893ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
894ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
895ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
896ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
897ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
898ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
899ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
900ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
901e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
902ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
903ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
904ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
905ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
906ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
907ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
908ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
909ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
910ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
911377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
912ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
913ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
914ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
915ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
916ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
917ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
918ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
919ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
920ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
921ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
922ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
923c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
924ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
925ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
926ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
927ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
928ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
929ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
930ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
931ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
932ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
933ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
934ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
935ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
936ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
937ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
938ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
939ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
940ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
941ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
942ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
943ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
94492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
94592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
9469c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
9479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
9489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
9499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
9509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
9519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
9529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public List<HdmiCecDeviceInfo> getInputDevices() {
9539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
9549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
9559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
9569c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
9579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
9589c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
9599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
9609c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
9619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
9629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
9639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
964160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setControlEnabled(final boolean enabled) {
96592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            enforceAccessPermission();
96692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            runOnServiceThread(new Runnable() {
96792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                @Override
96892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                public void run() {
9694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleHdmiControlStatusChanged(enabled);
9704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
97192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                }
97292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            });
97392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
974a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang
975a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        @Override
97641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
97741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
97841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
97941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
98041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
98141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
98241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
98341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
98441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
98541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
98641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
98741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
98841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
98941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
99041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
99141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
99241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
99341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
99441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
99541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
99641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
99741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
99841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
99941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
100041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
100141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
100241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
100341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
100441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
100541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
100641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
100741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
100841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
1009a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
1010a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
1011a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
1012a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
1013a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
1014a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
1015a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
101638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
1017a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
1018a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
1019a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
1020a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
1021a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
1022160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
1023160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
1024160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setOption(final int key, final int value) {
10254d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
1026160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            if (!isTvDevice()) {
1027160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                return;
1028160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
1029160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            switch (key) {
1030c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
103125c20298ad04e0e591e0cfdc0bb9d01a985433abYuncheol Heo                    tv().setAutoWakeup(value == HdmiTvClient.ENABLED);
1032160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(key, value);
1033160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1034c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF:
1035160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    // No need to pass this option to HAL.
1036c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED);
1037160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1038c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING:  // Fall through
1039c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_POWER_CHARGE:
1040160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
1041160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(key, value);
1042160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
1043160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
1044160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
1045160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
1046160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
10474d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        @Override
10484d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
10494d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
10504d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
10514d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
10524d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
10534d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
10544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
1055119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1056119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1057119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
1058119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
1059119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1060119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1061119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1062119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1063119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1064119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1065119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
1066119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1067119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1068119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1069119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1070119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1071119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1072119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1073119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1074119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1075119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1076119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1077119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1078119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1079119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1080119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1081119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1082119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1083119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1084119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1085119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1086119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1087119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1088119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1089119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
109012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1091a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1092a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
109312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
109412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1095b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1096b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1097b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1098b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1099b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1100b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1101b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1102b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1103b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1104b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1105b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1106b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1107b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1108b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1109b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1110b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1111b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1112b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1113b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1114b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1115b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1116b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1117b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1118b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1119b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1120b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1121b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1122b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1123a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1124a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1125a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1126b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1127b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1128b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1129b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1130b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1131b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1132b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1133b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1134b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1135b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1136b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1137b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1138bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1139bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1140bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1141b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1142b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1143b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1144b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1145b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1146b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1147b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1148b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1149b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1150b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1151b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1152b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1153a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
115478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
115578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1156a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
115779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
115879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
115979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
11607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
11617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1162c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
116579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
116678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
116778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1168a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
116979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
117079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
117179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
11727fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
11737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1174c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
117779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
117878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
117978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
118078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
118178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
118278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
118378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
118478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
118578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
118678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
118778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
118878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
118978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
119078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
119178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
119278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
119378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
119478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
119578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
119678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
119778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
119878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
119978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
120078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
120178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
120278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
120378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
120478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
120578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
12067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
12076d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
12084893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
12094893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
12104893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
12114893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
12124893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
12134893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
12144893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
12156d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
12164893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
12174893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
12184893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
12194893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
12204893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
12214893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
12224893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
12234893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
12244893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
12254893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
12264893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
12274893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
12286d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
12296d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
12306d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
12316d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
12326d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1233ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1234ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1235ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1236ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1237ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1238ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1239ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1240ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1241ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1242ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1243ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1244ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1245ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1246ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1247ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1248ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1249ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1250ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1251ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1252ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1253ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1254ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1255ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1256ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1257ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1258ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1259ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1260ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1261ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
12629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
12639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
12649c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
12659c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
12669c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
12679c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12689c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12699c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12709c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12719c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
12729c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12739c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
12749c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
12759c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
12769c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
12779c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
12789c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
12799c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12809c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
12819c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12829c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12839c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
128472b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim    void invokeInputChangeListener(HdmiCecDeviceInfo info) {
12859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
12879c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
128872b7d738d5b9254594726304cdb1777b54d95631Jinsuk Kim                    mInputChangeListener.onChanged(info);
12899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
12909c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
12919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
12929c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
129612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1297b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
129812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord();
1299b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
130012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1301b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
130212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1303b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
130412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListener = listener;
1305b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1306b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1307b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1308b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1309b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
131012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
131112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
131212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    return mRecordListener.getOneTouchRecordSource(recorderAddress);
131312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
131412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1315b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1316b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1317b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1318b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1319b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1320b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
132112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
132212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
132312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
132412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
132512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onOneTouchRecordResult(result);
132612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
132712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
132812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
132912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
133012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
133112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
133212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
133312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
133412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
133512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
133612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
133712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onTimerRecordingResult(result);
133812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
1339e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
1340e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                }
1341e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            }
1342e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        }
1343e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    }
1344e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang
1345e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang    void invokeClearTimerRecordingResult(int result) {
1346e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang        synchronized (mLock) {
1347e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang            if (mRecordListener != null) {
1348e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                try {
1349e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    mRecordListener.onClearTimerRecordingResult(result);
1350e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                } catch (RemoteException e) {
1351e5a9337ebe738633cf7b66141cdf76efcdc5754cJungshik Jang                    Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
135212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
135312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
135412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
135512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
135612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
13577fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
13587fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
13597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
13607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
13617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
13627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
13637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
136463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1365ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1366ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1367ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1368ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1369ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1370ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1371ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1372ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1373ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
13744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
13754893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
137660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
137760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
13784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
137960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
138060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
138160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
138260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
13834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
138460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
138560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
138660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
138760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
138860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
138960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1390e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1391e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
139279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
1393c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_TV);
139479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
139579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1396e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1397e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1398e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1399e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
140079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1401c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
1402c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_PLAYBACK);
140360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1404a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1405a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1406a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1407a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
140892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
140992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
141092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
141192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
141292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
141392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
141438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
141538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
141638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
141738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
141838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
141938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1420c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1421c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
142238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
142338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
142438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1425c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1426c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
142738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
142838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
142938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1430c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
143138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
143238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
143338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
143438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
143538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1436fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo        mWakeUpMessageReceived = true;
143738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
143838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
143938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
144038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
144138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
144238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
144338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
144438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
144538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
144638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
144738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
144838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
144938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
145038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
145138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
145238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
145338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
145438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
145538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1456c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
145738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1458a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1459fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                int startReason = INITIATED_BY_SCREEN_ON;
1460fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                if (mWakeUpMessageReceived) {
1461fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                    startReason = INITIATED_BY_WAKE_UP_MESSAGE;
1462fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                }
1463fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo                initializeCec(startReason);
1464a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
146538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
146638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
146738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
146838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
146938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
147038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
147138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
147238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
147338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1474c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
14754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
14774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
14784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
14794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
14804fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
14814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
14824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
14834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
14844b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // We will not clear local devices here, since some OEM/SOC will keep passing
14854b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // the received packets until the application processor enters to the sleep
14864b54271f1bbd29957c47433155c58aa792105d6dYuncheol Heo                    // actually.
14874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
14884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
14894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
14904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
14914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
149338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
14944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.disableDevice(mStandbyMessageReceived, callback);
149538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
149638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
149738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
149838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
14994fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
15004fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
15014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
15024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
15034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
15054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
15064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
15074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
15094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
151038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
15114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
15124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1513c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
151438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
151538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1516c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
151738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
15184fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
151938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
152038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
1521c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
152238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
15234d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1524119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1525119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1526119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1527119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1528119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1529119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1530119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1531119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1532119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1533119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1534119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1535119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1536119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1537119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1538119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1539119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1540119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1541119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1542119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1543119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1544119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1545119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1546119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1547119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1548119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1549119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1550119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1551119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1552119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
15534d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
15544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
15554d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
15564d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
15574d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
15584d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
15594d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
15604d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
15614d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
15624d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
15634d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
15644fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15654fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
15664fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleHdmiControlStatusChanged(boolean enabled) {
15674fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
15684fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15694fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED;
15704fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value);
15714fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mMhlController != null) {
15724fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value);
15734fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15744fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15754fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
15764fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
15774fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15784fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15794fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1580fc44e4e03c5f6486efb7457965dcf7eaf36bc971Yuncheol Heo            initializeCec(INITIATED_BY_ENABLE_CEC);
15814fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
15824fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
15834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
15844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
15854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
15864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
15874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
15884fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
15894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
15910792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1592