HdmiControlService.java revision f4eb72d53b4c5bc2286841006ad473ad4448bcf8
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;
574fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jangimport com.android.server.hdmi.HdmiCecLocalDevice.PendingActionClearedCallback;
580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
59b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jangimport libcore.util.EmptyArray;
60b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
6178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
62f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kimimport java.util.Arrays;
630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
6402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
65a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
660792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
670792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
680792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
690792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
700792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
73c7eba0f1db8928ca779933a564a06989e22a8532Jinsuk Kim    static final String PERMISSION = "android.permission.HDMI_CEC";
7478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
75d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
76d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
77d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
78d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
79d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
80d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
81d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
82ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
834fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <ul>
844fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_SUCCESS}
854fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_NAK}
864fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * <li>{@link Constants#SEND_RESULT_FAILURE}
874fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang         * </ul>
88d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
89d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
90d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
91d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
9202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
9302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
9402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
9502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
9602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
9702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
9802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
9902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
10002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
10102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
10202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
10302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
10438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
10538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
10638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
10738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
10838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
10938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
11038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
11138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
11238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
11338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
11438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
11538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
11638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
11738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
11838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
11938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
12038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
12138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
12778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
12878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
12978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
13278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
13378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
13478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1354893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
13678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
13778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
13878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1394893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
14078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
14178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
14278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1446d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1454893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1466d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1476d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1486d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1494893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
153119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    // List of records for vendor command listener to handle the the caller killed in action.
154119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
155119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
156119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
157119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1589c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1609c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1629c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1639c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
164b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
16512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private IHdmiRecordListener mRecordListener;
166b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
167b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    @GuardedBy("mLock")
16812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private HdmiRecordListenerRecord mRecordListenerRecord;
169b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
17092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
17192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
17292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
17392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
17492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1754d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1764d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1774d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1784d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
1794d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
1804d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
181ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
182ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
183ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
184ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
185ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
186ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
187ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
188ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1900340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1910340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1920792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
2000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
2010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2022b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from path(physical address) to port ID.
2032b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private SparseIntArray mPortIdMap = new SparseIntArray();
2042b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
2052b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    // Map from port ID to HdmiPortInfo.
2062b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
2072b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
20875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo    private HdmiCecMessageValidator mMessageValidator;
20975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo
21038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
21138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
21238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
213c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
21438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
21538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
21638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
21738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
2180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
2190792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
2200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
2210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
2220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2262f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
227c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
2287ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mProhibitMode = false;
2297ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
2308b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
231a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController = HdmiCecController.create(this);
2323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
233347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
234347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
235347a60449981fc934e5a84122df87c1447665548Yuncheol Heo                    HdmiTvClient.ENABLED);
236347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
237a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            // TODO: load value for mHdmiControlEnabled from preference.
238a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
239a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang                initializeCec(true);
240a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
241a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2420792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2430792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
245e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2492b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        initPortInfo();
25075a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        mMessageValidator = new HdmiCecMessageValidator(this);
2518692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
25263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
25338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
25438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
25538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
25638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
25738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
25838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
25938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
2607ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
26138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
2627ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    boolean readBooleanSetting(String key, boolean defVal) {
2637ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
2647ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        return Global.getInt(cr, key, defVal ? Constants.TRUE : Constants.FALSE) == Constants.TRUE;
2657ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    }
2667ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim
2677ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim    void writeBooleanSetting(String key, boolean value) {
2687ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        ContentResolver cr = getContext().getContentResolver();
2697ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim        Global.putInt(cr, key, value ? Constants.TRUE : Constants.FALSE);
2700792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
271e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
272a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    private void initializeCec(boolean fromBootup) {
273a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
274a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang                HdmiTvClient.ENABLED);
275a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang        initializeLocalDevices(mLocalDevices, fromBootup);
276a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    }
277a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang
278a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
279a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    private void initializeLocalDevices(final List<Integer> deviceTypes, final boolean fromBootup) {
280a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
2823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
2833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
28413c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim        mCecController.clearLogicalAddress();
2853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
2863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
2873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
2883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
2893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
2903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
2913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
292c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
2933ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
2943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
2953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
2963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
2973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
2983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
2993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
3003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
3023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3034893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
3040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
305c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
306c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
30738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        }
308a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang                        notifyAddressAllocated(devices, fromBootup);
3093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
3103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
3113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
3123ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3133ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
315a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
316a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices,
317a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            boolean fromBootup) {
318a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3193ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
3203ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
3213ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
322a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            device.handleAddressAllocated(address, fromBootup);
3233ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
3243ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
3270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
328a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
3292b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private void initPortInfo() {
330a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
3310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
3320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
3340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
3350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
3360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
3370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
3392b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return;
3402b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3412b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
3422b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        for (HdmiPortInfo info : cecPortInfo) {
3432b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            mPortIdMap.put(info.getAddress(), info.getId());
3442b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            mPortInfoMap.put(info.getId(), info);
3450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
347f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        if (mMhlController == null) {
348f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
349f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            return;
350f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim        } else {
351f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
352f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
353f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : mhlPortInfo) {
354f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (info.isMhlSupported()) {
355f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    mhlSupportedPorts.add(info.getId());
356f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
359f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            // Build HDMI port info list with CEC port info plus MHL supported flag.
360f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
361f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            for (HdmiPortInfo info : cecPortInfo) {
362f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                if (mhlSupportedPorts.contains(info.getId())) {
363f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
364f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                            info.isCecSupported(), true, info.isArcSupported()));
365f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                } else {
366f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                    result.add(info);
367f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim                }
3682b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            }
369f4eb72d53b4c5bc2286841006ad473ad4448bcf8Jinsuk Kim            mPortInfo = Collections.unmodifiableList(result);
3702b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        }
3710340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3720340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3730340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
3740340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
3750340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
3760340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
3770340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
3780340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
3792b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
3800340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
3812b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
3822b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortInfoMap.get(portId, null);
3830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
385e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
386401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
387401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
388401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
3892b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
390401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
3912b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
392401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
393401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
394401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
395c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
396401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
397401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
398401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
399401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
400401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
401401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
402401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
403401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
404401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
405401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
4062b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
407401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
4082b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
409c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
4102b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
411401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
412401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
4132b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
41409ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    boolean isValidPortId(int portId) {
4152b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
4162b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return getPortInfo(portId) != null;
41709ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim    }
41809ffc846af78f949d2847003db9f793bfb5eefaaJinsuk Kim
419401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
420e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
421e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
422e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
423e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
424e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
425e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
426e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
427e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
428e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
429e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
430e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
431e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
432e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
433e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
434e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
43567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
436e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
437c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
438c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
4393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
4403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
4423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
4433ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
4463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
4473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
4493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
4503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
452a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
453a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
4540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
45579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
45679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
45779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
45879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
45979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
460a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
461a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
4623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
463092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
464092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
465092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
466092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
467092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
468092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
469092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
47060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
47160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
4722b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    @ServiceThreadOnly
47360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
4742b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        assertRunOnServiceThread();
4752b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        int portId = mPortIdMap.get(physicalAddress);
4762b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        if (portId != Constants.INVALID_PORT_ID) {
4772b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim            return mPortInfoMap.get(portId).isArcSupported();
47860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
47960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
48060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
48160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
48279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
48367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
48467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
48567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
48663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
48763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
48863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
48963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
49063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
49163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
49263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
49363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
49463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
49563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
49667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
497c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
498c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
499c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
500d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
501c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
502a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
503d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
504a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
505d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
506d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
507d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
508a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
509d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
510a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
511d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
512c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
513c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
514a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
515a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
516a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
51775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        if (!mMessageValidator.isValid(message)) {
51875a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo            return false;
51975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        }
520092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
521092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
522092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
52379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
52479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
52560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
52660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
527a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
528092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
529a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
530092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
53179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
532c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
533092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
534092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
535092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
53660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
537c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
5383a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
5393a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
540092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
541a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
542a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
54367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
54467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
54567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
5468b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
54767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
54867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
549a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
55067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
55160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
55279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
55379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
55460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
55560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
55667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
55767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
55802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
55902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
56002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
56102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
56202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
5631de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
5640f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
56502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
5660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
56702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
568a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5691de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
5701de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
571a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5721de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
5731de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
5740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
5750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
5760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
577c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
5780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
5790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
5800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
581c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
5820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
5830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
5840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
5850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
58602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
58702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
58860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
58960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
59060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
59160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
59260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
59379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
59479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
59579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
59679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
59779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
598b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        AudioManager audioManager = getAudioManager();
599b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
600b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        if (mute) {
601b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (!muted) {
602b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
603b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
604b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        } else {
605b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            if (muted) {
606b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
607b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            }
608b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
609b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            // volume change notification back to hdmi control service.
610b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
611b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_SHOW_UI |
612b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang                    AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
613b69aafbfaddd8a6ac84b366b5db640cdd7e95354Jungshik Jang        }
6143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6153ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
616ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
617ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
618ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
619ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
620ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
621ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
6223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
62342c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
62442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
6253ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
6262b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType,
6272b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                getVendorId(), displayName);
6283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
6293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
63078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
63178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
63278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
63378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
63478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
63578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
63678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
63778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
63878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
63978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
64078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
64178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
64278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
64378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
64478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
64578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
64678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
64778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
6486d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
6496d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
6506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
6526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
6536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
6556d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
656ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
6576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
6586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
6596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
6606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
6616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
6636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
664ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
66538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
666ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
667ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
668ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
669ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
670ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
671ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
672ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
673ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
674ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
675ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
676ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
677ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
678ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
679ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
680119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
681119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
682119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
683119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
684119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
685119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
686119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
687119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
688119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
689119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
690119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
691119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
692119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
693119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
694119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
695119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
696119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
69712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private class HdmiRecordListenerRecord implements IBinder.DeathRecipient {
698b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
699b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void binderDied() {
700b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            synchronized (mLock) {
70112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                mRecordListener = null;
702b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
703b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
704b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
705b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
70678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
70778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
70878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
70978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
71078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
71178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
71278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
71378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
7150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
7160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
7170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
71878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
7190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
72078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
72178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
72278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
723a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
724a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
725a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
726a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
727a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
72879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
729a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
730a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
731c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
732a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
733a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
734a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
735a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
736a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
737a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
738a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
739a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
740a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
741a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
742a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
743a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
744a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
745a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
746a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
747a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
748c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
749a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
750a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
7518333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
752a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
753a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
754a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
755a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
756a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
757c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
758a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
759a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
760a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
761a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
762c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
763c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    if (localDevice == null) {
764c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                        Slog.w(TAG, "Local device not available");
765a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
766a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
767c068bb5a0468bf605b0398e6f0ea5721917de4eeJinsuk Kim                    localDevice.sendKeyEvent(keyCode, isPressed);
768a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
769a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
770a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
771a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
772a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
7737fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
77478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7757fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7767fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7777fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
7797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
78178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
78278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
78378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
78578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
7907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
79278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
79378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
79478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
79678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
8017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
80378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
80478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
80578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
8067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
80778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
8087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
8097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
8107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
8117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
8127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
8137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
81478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
8156d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8166d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8176d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
8186d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8196d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
8206d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
821ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
8226d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
8236d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
8246d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
8256d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
8266d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
8276d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
8286d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
8296d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
8306d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
8316d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
832ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
833ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
834ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
835ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
836ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
837ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
838ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
839ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
840e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
841ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
842ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
843ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
844ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
845ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
846ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
847ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
848ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
849ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
850377dcbd53af4529c352d453424539b069909fce4Jungshik Jang            return tv.isSystemAudioActivated();
851ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
852ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
853ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
854ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
855ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
856ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
857ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
858ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
859ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
860ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
861ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
862c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
863ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
864ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
865ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
866ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
867ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
868ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
869ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
870ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
871ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
872ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
873ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
874ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
875ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
876ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
877ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
878ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
879ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
880ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
881ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
882ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
88392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
88492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
8859c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
8869c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
8879c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
8889c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
8899c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
8909c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
8919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public List<HdmiCecDeviceInfo> getInputDevices() {
8929c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
8939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
8949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
8959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
8969c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
8979c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
8989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
8999c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
9009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
9019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
9029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
903160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setControlEnabled(final boolean enabled) {
90492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            enforceAccessPermission();
90592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            runOnServiceThread(new Runnable() {
90692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                @Override
90792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                public void run() {
9084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    handleHdmiControlStatusChanged(enabled);
9094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
91092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                }
91192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            });
91292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
913a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang
914a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        @Override
91541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
91641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
91741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
91841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
91941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
92041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
92141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
92241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
92341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
92441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
92541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
92641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
92741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
92841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
92941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
93041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
93141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
93241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
93341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
93441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
93541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
93641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
93741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
93841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
93941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
94041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
94141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
94241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
94341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
94441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
94541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
94641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
94741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
948a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
949a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
950a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
951a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
952a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
953a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
954a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
95538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
956a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
957a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
958a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
959a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
960a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
961160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
962160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
963160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setOption(final int key, final int value) {
9644d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
965160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            if (!isTvDevice()) {
966160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                return;
967160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
968160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            switch (key) {
969c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
970160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(key, value);
971160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
972c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF:
973160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    // No need to pass this option to HAL.
974c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED);
975160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
976c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING:  // Fall through
977c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_POWER_CHARGE:
978160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
979160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(key, value);
980160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
981160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
982160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
983160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
984160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
9854d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        @Override
9864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
9874d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
9884d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
9894d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
9904d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
9914d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
9924d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
993119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
994119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
995119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
996119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
997119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
998119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
999119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1000119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1001119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
1002119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1003119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
1004119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1005119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1006119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
1007119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
1008119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
1009119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
1010119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
1011119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
1012119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
1013119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
1014119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
1015119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
1016119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
1017119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1018119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
1019119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
1020119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
1021119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
1022119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
1023119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
1024119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
1025119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
1026119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1027119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
102812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
1029a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1030a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
103112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        public void setHdmiRecordListener(IHdmiRecordListener listener) {
103212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            HdmiControlService.this.setHdmiRecordListener(listener);
1033b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1034b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1035b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1036b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
1037b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1038b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1039b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1040b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1041b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1042b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1043b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1044b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startOneTouchRecord(recorderAddress, recordSource);
1045b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1046b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1047b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1048b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1049b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        @Override
1050b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void stopOneTouchRecord(final int recorderAddress) {
1051b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1052b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1053b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1054b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1055b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1056b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1057b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1058b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().stopOneTouchRecord(recorderAddress);
1059b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1060b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1061a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
1062a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang
1063a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        @Override
1064b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void startTimerRecording(final int recorderAddress, final int sourceType,
1065b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1066b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1067b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1068b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1069b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1070b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1071b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1072b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1073b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().startTimerRecording(recorderAddress, sourceType, recordSource);
1074b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1075b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1076bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        }
1077bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang
1078bffb0635aaaaf9140d9120e3f3d95a4f7391a0acJungshik Jang        @Override
1079b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        public void clearTimerRecording(final int recorderAddress, final int sourceType,
1080b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                final byte[] recordSource) {
1081b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            runOnServiceThread(new Runnable() {
1082b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                @Override
1083b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                public void run() {
1084b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    if (!isTvDevice()) {
1085b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        Slog.w(TAG, "No TV is available.");
1086b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                        return;
1087b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    }
1088b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                    tv().clearTimerRecording(recorderAddress, sourceType, recordSource);
1089b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1090b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            });
1091a6b2a7a59ab79b2d91412c1095d1c49b8dc9d507Jungshik Jang        }
109278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
109378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1094a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
109579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
109679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
109779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
10987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
10997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1100c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
110379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
110478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
110578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1106a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
110779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
110879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
110979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
11107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
11117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1112c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
11137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
11147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
111579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
111678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
111778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
111878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
111978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
112078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
112178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
112278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
112378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
112478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
112578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
112678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
112778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
112878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
112978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
113078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
113178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
113278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
113378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
113478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
113578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
113678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
113778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
113878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
113978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
114078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
114178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
114278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
114378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
11447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
11456d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
11464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
11474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
11484893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
11494893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
11504893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
11514893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
11524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
11536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
11544893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
11554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
11564893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
11574893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
11584893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
11594893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
11604893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
11614893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
11624893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
11634893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
11644893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
11654893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
11666d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
11676d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
11686d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
11696d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
11706d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1171ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1172ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1173ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1174ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1175ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1176ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1177ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1178ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1179ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1180ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1181ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1182ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1183ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1184ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1185ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1186ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1187ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1188ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1189ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1190ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1191ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1192ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1193ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1194ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1195ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1196ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1197ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1198ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1199ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
12009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
12019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
12029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
12039c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
12049c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
12059c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12069c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12079c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12099c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
12109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12119c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
12129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
12139c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
12149c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
12159c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
12169c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
12179c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12189c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
12199c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12209c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12219c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
12229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    void invokeInputChangeListener(int activeAddress) {
12239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
12249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
12259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                HdmiCecDeviceInfo activeSource = getDeviceInfo(activeAddress);
12269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
12279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    mInputChangeListener.onChanged(activeSource);
12289c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
12299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
12309c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
12319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
12329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
12339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
12349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
123512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    private void setHdmiRecordListener(IHdmiRecordListener listener) {
1236b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
123712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListenerRecord = new HdmiRecordListenerRecord();
1238b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            try {
123912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                listener.asBinder().linkToDeath(mRecordListenerRecord, 0);
1240b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            } catch (RemoteException e) {
124112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                Slog.w(TAG, "Listener already died.", e);
1242b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
124312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            mRecordListener = listener;
1244b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1245b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1246b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
1247b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    byte[] invokeRecordRequestListener(int recorderAddress) {
1248b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        synchronized (mLock) {
124912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
125012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
125112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    return mRecordListener.getOneTouchRecordSource(recorderAddress);
125212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
125312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to start record.", e);
1254b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang                }
1255b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            }
1256b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang            return EmptyArray.BYTE;
1257b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang        }
1258b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang    }
1259b6591b8e5399099dc6b7693e0fc719b613aba89cJungshik Jang
126012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeOneTouchRecordResult(int result) {
126112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
126212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
126312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
126412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onOneTouchRecordResult(result);
126512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
126612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
126712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
126812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
126912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
127012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
127112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
127212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    void invokeTimerRecordingResult(int result) {
127312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        synchronized (mLock) {
127412e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            if (mRecordListener != null) {
127512e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                try {
127612e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    mRecordListener.onTimerRecordingResult(result);
127712e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                } catch (RemoteException e) {
127812e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                    Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
127912e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang                }
128012e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang            }
128112e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang        }
128212e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang    }
128312e5dcefe136b58562f39604e6a8460ac92cb895Jungshik Jang
12847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
12857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
12867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
12877fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
12887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
12897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
12907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
129163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1292ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1293ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1294ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1295ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1296ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1297ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1298ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1299ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1300ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
13014893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
13024893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
130360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
130460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
13054893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
130660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
130760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
130860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
130960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
13104893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
131160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
131260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
131360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
131460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
131560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
131660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1317e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1318e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
131960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
1320c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (path1 & Constants.ROUTING_PATH_TOP_MASK)
1321c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                == (path2 & Constants.ROUTING_PATH_TOP_MASK);
132260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
132360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
132479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
1325c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_TV);
132679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
132779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
1328e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    boolean isTvDevice() {
1329e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo        return tv() != null;
1330e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo    }
1331e946ed8f54c5ee0e58e168df00d4f418e1eed7a7Yuncheol Heo
133279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1333c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
1334c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_PLAYBACK);
133560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1336a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1337a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1338a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1339a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
134092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
134192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
134292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
134392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
134492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
134592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
134638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
134738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
134838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
134938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
135038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
135138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1352c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1353c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
135438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
135538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
135638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1357c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1358c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
135938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
136038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
136138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1362c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
136338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
136438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
136538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
136638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
136738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
136838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
136938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
137038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
137138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
137238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
137338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
137438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
137538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
137638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
137738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
137838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
137938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
138038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
138138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
138238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
138338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
138438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
138538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
138638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1387c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
138838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1389a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            if (mHdmiControlEnabled) {
1390a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang                initializeCec(true);
1391a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            }
139238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
139338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
139438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
139538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
139638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
139738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
139838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
139938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
140038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1401c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
14024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
14044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        disableDevices(new PendingActionClearedCallback() {
14054fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            @Override
14064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            public void onCleared(HdmiCecLocalDevice device) {
14074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
14084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                devices.remove(device);
14094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                if (devices.isEmpty()) {
14104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
14114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    onStandbyCompleted();
14124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
14134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            }
14144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        });
14154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
14164fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14174fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void disableDevices(PendingActionClearedCallback callback) {
141838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
14194fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.disableDevice(mStandbyMessageReceived, callback);
142038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
142138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
142238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
142338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
14244fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void clearLocalDevices() {
14254fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
14264fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mCecController == null) {
14274fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            return;
14284fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
14294fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLogicalAddress();
14304fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.clearLocalDevices();
14314fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
14324fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14334fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
14344fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void onStandbyCompleted() {
143538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
14364fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        Slog.v(TAG, "onStandbyCompleted");
14374fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
1438c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
143938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
144038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1441c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
144238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
14434fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            device.onStandby(mStandbyMessageReceived);
144438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
144538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
1446c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
144738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
14484d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1449119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1450119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1451119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1452119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1453119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1454119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1455119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1456119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1457119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1458119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1459119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1460119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1461119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1462119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1463119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1464119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1465119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1466119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1467119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1468119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1469119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1470119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1471119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1472119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1473119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1474119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1475119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1476119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1477119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
14784d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
14794d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
14804d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
14814d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
14824d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
14834d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
14844d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
14854d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
14864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
14874d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
14884d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
14894fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14904fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    @ServiceThreadOnly
14914fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    private void handleHdmiControlStatusChanged(boolean enabled) {
14924fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        assertRunOnServiceThread();
14934fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
14944fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED;
14954fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value);
14964fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (mMhlController != null) {
14974fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value);
14984fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
14994fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15004fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        synchronized (mLock) {
15014fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            mHdmiControlEnabled = enabled;
15024fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15034fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang
15044fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        if (enabled) {
1505a9f10629f4bc1a82761917645ff4d2b6d42e47b3Jungshik Jang            initializeCec(false);
15064fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        } else {
15074fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            disableDevices(new PendingActionClearedCallback() {
15084fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                @Override
15094fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                public void onCleared(HdmiCecLocalDevice device) {
15104fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    assertRunOnServiceThread();
15114fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                    clearLocalDevices();
15124fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang                }
15134fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang            });
15144fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang        }
15154fc1d105fc279bf7df6c876e160672866bdad8e7Jungshik Jang    }
15160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1517