HdmiControlService.java revision 347a60449981fc934e5a84122df87c1447665548
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;
210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
2238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.Intent;
2338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.content.IntentFilter;
24c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
25c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiControlManager;
2660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
28c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.hardware.hdmi.HdmiTvClient;
29d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
30d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
316d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kimimport android.hardware.hdmi.IHdmiDeviceEventListener;
32d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kimimport android.hardware.hdmi.IHdmiInputChangeListener;
34ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jangimport android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
35119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kimimport android.hardware.hdmi.IHdmiVendorCommandListener;
36a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
3742c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
3867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
4078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
41e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
4238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
4378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
4438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
478b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
4878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
494893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
51a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
5602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
57a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
610792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
620792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
630792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
640792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
6578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
6678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
6778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
68d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
69d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
70d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
74d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
75ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
76ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
77ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
78ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
79d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
80d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
81d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
82d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
8702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
8902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
9002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
9102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
9202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
9302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
9402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
9538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
9638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
9738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
9838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
9938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
10038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
10138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
10238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
10338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
10438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
10538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
10638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
10738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
10838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
10938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
11038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
11138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
11238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
11878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
11978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
12078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
12378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
12478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
12578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1264893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
12778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
12878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
12978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1304893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
13178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
13278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
13378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1346d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1356d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1364893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1376d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1386d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1396d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1404893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1416d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1426d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1436d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
144119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    // List of records for vendor command listener to handle the the caller killed in action.
145119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    @GuardedBy("mLock")
146119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
147119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            new ArrayList<>();
148119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
15592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
15692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
15792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
15892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
15992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1604d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1614d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1624d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1634d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
1644d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
1654d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
166ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
167ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
168ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
169ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
170ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
171ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
172ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
173ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1750340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1760340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1770792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1780792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1790792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1800792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1810792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1820792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
1850340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
1860340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
18738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
18838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
18938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
190c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
19138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
19238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
19338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
19438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
1970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
1980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
1990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
2000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
2010792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
2020792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
2032f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
204c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
205e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
2068b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
2073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
208347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            // TODO: Remove this as soon as OEM's HAL implementation is corrected.
209347a60449981fc934e5a84122df87c1447665548Yuncheol Heo            mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
210347a60449981fc934e5a84122df87c1447665548Yuncheol Heo                    HdmiTvClient.ENABLED);
211347a60449981fc934e5a84122df87c1447665548Yuncheol Heo
212c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
213347a60449981fc934e5a84122df87c1447665548Yuncheol Heo                    HdmiTvClient.ENABLED);
2143ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
215a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2170792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2180792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
219e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2210792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mPortInfo = initPortInfo();
2248692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
22563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
22638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
22738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
22838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
22938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
23038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
23138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
23238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
23338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
23463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
23563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
23692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        mHdmiControlEnabled = true;
2374d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        // TODO: Get control flag from persistent storage
2384d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        mProhibitMode = false;
2390792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
240e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
241a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private void initializeLocalDevices(final List<Integer> deviceTypes) {
243a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
2453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
2463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
24713c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim        mCecController.clearLogicalAddress();
2483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
2493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
2503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
2513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
2523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
2533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
2543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
255c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    if (logicalAddress == Constants.ADDR_UNREGISTERED) {
2563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
2573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
2583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
2593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
2603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
2613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
2623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
2633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
2643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
2653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2664893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
2670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
268c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
269c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                            mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
27038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        }
2713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
2723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
2733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
2743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
2753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
278a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
280a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
2823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
2833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
28413c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim            device.handleAddressAllocated(address);
2853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2880340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
2890340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
290a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2910340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> initPortInfo() {
292a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
2940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
2960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
2970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
2980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
2990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
3010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return Collections.emptyList();
3020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
3050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mMhlController != null) {
3060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // TODO: Implement plumbing logic to get MHL port information.
3070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mhlPortInfo = mMhlController.getPortInfos();
3080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // Use the id (port number) to find the matched info between CEC and MHL to combine them
3110340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
3120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
3130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int i = 0; i < cecPortInfo.length; ++i) {
3140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            HdmiPortInfo cec = cecPortInfo[i];
3150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int id = cec.getId();
3160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            boolean mhlInfoFound = false;
3170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (HdmiPortInfo mhl : mhlPortInfo) {
3180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                if (id == mhl.getId()) {
3190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
3200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
3210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    mhlInfoFound = true;
3220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    break;
3230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                }
3240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (!mhlInfoFound) {
3260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                result.add(cec);
3270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(result);
3310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
3340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
3350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
3360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
3370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
3380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
3390340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
3400340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // mPortInfo is an unmodifiable list and the only reference to its inner list.
3410340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // No lock is necessary.
3420340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
3430340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (portId == info.getId()) {
3440340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                return info;
3450340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return null;
3480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
350e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
351401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
352401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
353401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
354401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
355401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
356401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
357401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
358c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return Constants.INVALID_PHYSICAL_ADDRESS;
359401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
360401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
361401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
362401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
363401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
364401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
365401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
366401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
367401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
368401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
369401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
370c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
371401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
372401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            if (portAddress == info.getAddress()) {
373401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim                return info.getId();
374401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            }
375401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
376c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return Constants.INVALID_PORT_ID;
377401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
378401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
379401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
380e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
381e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
382e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
383e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
384e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
385e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
386e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
387e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
388e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
389e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
390e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
391e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
392e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
393e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
394e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
39567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
396e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
397c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
398c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
3993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
4003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
4023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
4033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
4063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
4073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4083ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
4093ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
4103ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
412a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
413a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
4140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
41579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
41679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
41779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
41879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
41979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
420a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
421a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
4223ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
423092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
424092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
425092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
426092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
427092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
428092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
429092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
43060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
43160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
43260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
43360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        for (HdmiPortInfo portInfo : mPortInfo) {
43460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
43560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                    && portInfo.isArcSupported()) {
43660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return true;
43760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
43860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
43960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
44060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
44160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
44279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
44367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
44467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
44567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
44663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
44763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
44863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
44963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
45063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
45163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
45263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
45363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
45463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
45563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
45667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
457c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
458c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
459c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
460d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
461c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
462a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
463d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
464a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
465d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
466d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
467d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
468a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
469d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
470a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
471d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
472c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
473c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
474a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
475a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
476a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
477092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
478092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
479092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
48079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
48179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
48260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
48360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
484a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
485092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
486a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
487092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
48879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
489c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    && message.getDestination() != Constants.ADDR_BROADCAST) {
490092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
491092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
492092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
49360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
494c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (message.getDestination() != Constants.ADDR_BROADCAST) {
4953a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
4963a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
497092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
498a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
499a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
50067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
50167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
50267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
5038b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
50467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
50567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
506a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
50767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
50860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
50979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
51079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
51160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
51260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
51367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
51467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
51502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
51602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
51702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
51802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
51902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
5201de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
5210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
52202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
5230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
52402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
525a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5261de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
5271de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
528a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5291de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
5301de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
5310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
5320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
5330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
534c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
5350f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
5360f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
5370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
538c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int iterationStrategy = pickStrategy & Constants.POLL_ITERATION_STRATEGY_MASK;
5390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
5400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
5410f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
5420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
54302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
54402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
54560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
54660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
54760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
54860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
54960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
55079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
55179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
55279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
55379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
55479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
55579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // TODO: Hook up with AudioManager.
5563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
558ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
559ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
560ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
561ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
562ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
563ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
5643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
56542c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
56642c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
5673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
5683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
5693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
57878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
57978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
58078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
58178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
58278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
58378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
58478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
58578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
58678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
58778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
58878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
5896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
5906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
5916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
5936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
5946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
597ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
5986d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
5996d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
6006d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
6016d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
6026d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
6036d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
6046d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
605ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
60638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
607ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
608ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
609ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
610ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
611ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
612ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
613ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
614ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
615ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
616ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
617ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
618ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
619ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
620ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
621119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    class VendorCommandListenerRecord implements IBinder.DeathRecipient {
622119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final IHdmiVendorCommandListener mListener;
623119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        private final int mDeviceType;
624119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
625119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
626119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mListener = listener;
627119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mDeviceType = deviceType;
628119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
629119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
630119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
631119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void binderDied() {
632119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            synchronized (mLock) {
633119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                mVendorCommandListenerRecords.remove(this);
634119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
635119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
636119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
637119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
63878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
63978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
64078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
64178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
64278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
64378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
64478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
64578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6460340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
6470340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
6480340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
6490340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
65078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
6510340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
65278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
65378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
65478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
655a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
656a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
657a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
658a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
659a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
66079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
661a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
662a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
663c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
664a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
665a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
666a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
667a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
668a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
669a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
670a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
671a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
672a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
673a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
674a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
675a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
676a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
677a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
678a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
679a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
680c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
681a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
682a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
6838333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
684a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
685a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
686a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
687a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
688a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
689a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
690a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
691a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
692a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
693a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
694a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    // TODO: sendKeyEvent is for TV device only for now. Allow other
695a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    //       local devices of different types to use this as well.
696a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
697a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
698a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
699a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
700a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
701a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    tv.sendKeyEvent(keyCode, isPressed);
702a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
703a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
704a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
705a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
706a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
7077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
70878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7097fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7107fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
7137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
71578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
71678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
71778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
71978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7207fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7217fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7227fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
7247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
72678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
72778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
72878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
73078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
7357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
73778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
73878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
73978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
74178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7457fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
7467fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7477fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
74878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
7496d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
7506d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
7516d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
7526d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
7536d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
7546d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
755ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
7566d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
7576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
7586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
7596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
7606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
7616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
7626d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
7636d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
7646d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
7656d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
766ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
767ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
768ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
769ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
770ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
771ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
772ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
773ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
774e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
775ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
776ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
777ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
778ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
779ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
780ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
781ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
782ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
783ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
784ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return tv.getSystemAudioMode();
785ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
786ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
787ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
788ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
789ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
790ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
791ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
792ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
793ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
794ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
795ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
796c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
797ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
798ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
799ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
800ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
801ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
802ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
803ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
804ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
805ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
806ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
807ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
808ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
809ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
810ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
811ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
812ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
813ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
814ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
815ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
816ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
81792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
81892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
8199c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
8209c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
8219c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
8229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
8239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
8249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
8259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public List<HdmiCecDeviceInfo> getInputDevices() {
8269c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
8279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
8289c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
8299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
8309c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
8319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
8329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
8339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
8349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
8359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
8369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
837160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setControlEnabled(final boolean enabled) {
83892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            enforceAccessPermission();
83992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            synchronized (mLock) {
84092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                mHdmiControlEnabled = enabled;
84192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
84292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            // TODO: Stop the running actions when disabled, and start
84392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            //       address allocation/device discovery when enabled.
84492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (!enabled) {
84592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return;
84692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
84792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            runOnServiceThread(new Runnable() {
84892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                @Override
84992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                public void run() {
85092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
85192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    if (tv == null) {
85292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                        return;
85392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    }
854c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED;
855c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value);
856160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
857c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                        mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value);
858160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
8595344cd98e69f92e70d52969b1851c9d8f9e81853Jinsuk Kim                    tv.launchRoutingControl(false);
86092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                }
86192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            });
86292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
863a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang
864a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        @Override
86541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
86641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
86741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
86841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
86941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
87041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
87141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
87241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
87341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
87441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
87541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
87641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
87741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
87841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
87941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
88041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
88141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
88241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
88341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
88441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
88541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
88641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
88741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
88841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
88941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
89041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
89141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
89241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
89341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
89441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
89541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
89641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
89741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
898a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
899a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
900a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
901a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
902a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
903a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
904a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
90538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
906a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
907a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
908a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
909a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
910a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
911160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
912160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
913160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setOption(final int key, final int value) {
9144d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
915160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            if (!isTvDevice()) {
916160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                return;
917160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
918160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            switch (key) {
919c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
920160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(key, value);
921160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
922c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF:
923160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    // No need to pass this option to HAL.
924c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                    tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED);
925160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
926c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING:  // Fall through
927c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                case HdmiTvClient.OPTION_MHL_POWER_CHARGE:
928160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
929160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(key, value);
930160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
931160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
932160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
933160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
934160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
935160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        private boolean isTvDevice() {
936160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            return tv() != null;
937160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
9384d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
9394d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        @Override
9404d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
9414d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
9424d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
9434d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
9444d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
9454d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
9464d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
947119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
948119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
949119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
950119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final int deviceType) {
951119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
952119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
953119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
954119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
955119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiControlService.this.addVendorCommandListener(listener, deviceType);
956119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
957119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
958119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
959119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
960119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        @Override
961119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        public void sendVendorCommand(final int deviceType, final int targetAddress,
962119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                final byte[] params, final boolean hasVendorId) {
963119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            enforceAccessPermission();
964119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            runOnServiceThread(new Runnable() {
965119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                @Override
966119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                public void run() {
967119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
968119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (device == null) {
969119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        Slog.w(TAG, "Local device not available");
970119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        return;
971119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
972119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    if (hasVendorId) {
973119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommandWithId(
974119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress,
975119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                getVendorId(), params));
976119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    } else {
977119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                        sendCecCommand(HdmiCecMessageBuilder.buildVendorCommand(
978119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                                device.getDeviceInfo().getLogicalAddress(), targetAddress, params));
979119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    }
980119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
981119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            });
982119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim         }
98378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
98478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
985a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
98679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
98779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
98879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
9897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
9907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
991c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
9927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
9937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
99479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
99578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
99678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
997a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
99879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
99979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
100079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
10017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
10027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
1003c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
10047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
10057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
100679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
100778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
100878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
100978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
101078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
101178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
101278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
101378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
101478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
101578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
101678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
101778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
101878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
101978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
102078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
102178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
102278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
102378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
102478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
102578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
102678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
102778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
102878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
102978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
103078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
103178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
103278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
103378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
103478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
10357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
10366d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
10374893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
10384893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
10394893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
10404893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
10414893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
10424893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
10434893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
10446d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
10454893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
10464893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
10474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
10484893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
10494893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
10504893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
10514893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
10524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
10534893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
10544893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
10554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
10564893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
10576d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
10586d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
10596d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
10606d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
10616d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1062ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
1063ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1064ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1065ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1066ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1067ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1068ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1069ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1070ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1071ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1072ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1073ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1074ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1075ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1076ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1077ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1078ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1079ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1080ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1081ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1082ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1083ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1084ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1085ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1086ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1087ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1088ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1089ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1090ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
10919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
10929c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
10939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
10949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
10959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
10969c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
10979c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
10989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
10999c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
11019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
11029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
11039c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
11049c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
11059c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
11069c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
11079c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
11089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
11099c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
11109c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11119c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
11129c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11139c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    void invokeInputChangeListener(int activeAddress) {
11149c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
11159c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
11169c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                HdmiCecDeviceInfo activeSource = getDeviceInfo(activeAddress);
11179c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
11189c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    mInputChangeListener.onChanged(activeSource);
11199c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
11209c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
11219c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
11229c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
11239c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
11249c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
11259c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
11267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
11277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
11287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
11297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
11307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
11317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
11327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
113363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1134ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1135ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1136ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1137ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1138ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1139ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1140ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1141ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1142ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
11434893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
11444893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
114560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
114660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
11474893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
114860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
114960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
115060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
115160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
11524893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
115360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
115460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
115560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
115660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
115760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
115860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1159e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1160e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
116160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
1162c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (path1 & Constants.ROUTING_PATH_TOP_MASK)
1163c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                == (path2 & Constants.ROUTING_PATH_TOP_MASK);
116460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
116560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
116679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
1167c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_TV);
116879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
116979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
117079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
1171c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (HdmiCecLocalDevicePlayback)
1172c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                mCecController.getLocalDevice(HdmiCecDeviceInfo.DEVICE_PLAYBACK);
117360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1174a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1175a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1176a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1177a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
117892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
117992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
118092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
118192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
118292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
118392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
118438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
118538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
118638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
118738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
118838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
118938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
1190c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
1191c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
119238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
119338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
119438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
1195c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY
1196c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
119738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
119838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
119938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
1200c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
120138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
120238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
120338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
120438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
120538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
120638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
120738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
120838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
120938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
121038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
121138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
121238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
121338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
121438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
121538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
121638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
121738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
121838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
121938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
122038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
122138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
122238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
122338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
122438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1225c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
122638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
1227c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.ENABLED);
122838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            initializeLocalDevices(mLocalDevices);
122938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
123038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
123138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
123238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
123338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
123438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
123538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
123638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
123738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1238c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
123938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
124038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            device.onTransitionToStandby(mStandbyMessageReceived);
124138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
124238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
124338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
124438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
124538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * Called when there are the outstanding actions in the local devices.
124638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * This callback is used to wait for when the action queue is empty
124738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * during the power state transition to standby.
124838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
124938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
125038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void onPendingActionsCleared() {
125138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
1252c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
125338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
125438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
1255c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
125638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
125738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            device.onStandBy(mStandbyMessageReceived);
125838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
125938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
1260c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
126138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
12624d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
1263119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
1264119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
1265119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        try {
1266119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            listener.asBinder().linkToDeath(record, 0);
1267119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        } catch (RemoteException e) {
1268119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            Slog.w(TAG, "Listener already died");
1269119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            return;
1270119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1271119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1272119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            mVendorCommandListenerRecords.add(record);
1273119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1274119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1275119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
1276119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    void invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params,
1277119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            boolean hasVendorId) {
1278119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        synchronized (mLock) {
1279119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
1280119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                if (record.mDeviceType != deviceType) {
1281119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    continue;
1282119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1283119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                try {
1284119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    record.mListener.onReceived(srcAddress, params, hasVendorId);
1285119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                } catch (RemoteException e) {
1286119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                    Slog.e(TAG, "Failed to notify vendor command reception", e);
1287119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim                }
1288119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim            }
1289119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim        }
1290119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim    }
1291119160a68195bcb2f5bdf4a269807e01228eca97Jinsuk Kim
12924d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
12934d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
12944d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
12954d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
12964d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
12974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
12984d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
12994d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
13004d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
13014d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
13024d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
13030792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1304