HdmiControlService.java revision 5344cd98e69f92e70d52969b1851c9d8f9e81853
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;
24a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport android.hardware.hdmi.HdmiCec;
25c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
26c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecMessage;
2760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jangimport android.hardware.hdmi.HdmiHotplugEvent;
280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport android.hardware.hdmi.HdmiPortInfo;
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;
35a858d221ff86c497e745222ea15bab141e337636Jungshik Jangimport android.media.AudioManager;
3642c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jangimport android.os.Build;
3767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
380792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
3978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
40e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
4138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.PowerManager;
4278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
4338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heoimport android.os.SystemClock;
440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
468b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
4778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
484893c7efde52411ad051ef5c20251439f4098eacJinsuk Kimimport com.android.internal.annotations.GuardedBy;
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
50a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jangimport com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
540340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
5502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
56a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
570792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
610792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
620792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
630792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
6478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
6578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
6678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
67d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
68d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
69d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
70d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
74ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
75ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
76ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
77ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
78d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
79d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
80d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
81d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
8202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
8302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
8802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
8902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
9002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
9102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
9202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
9302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
9438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private class PowerStateReceiver extends BroadcastReceiver {
9538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        @Override
9638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        public void onReceive(Context context, Intent intent) {
9738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            switch (intent.getAction()) {
9838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_OFF:
9938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerOnOrTransient()) {
10038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onStandby();
10138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
10238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
10338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                case Intent.ACTION_SCREEN_ON:
10438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    if (isPowerStandbyOrTransient()) {
10538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        onWakeUp();
10638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    }
10738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                    break;
10838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            }
10938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
11038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
11138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1120792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
1130792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
1140792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
1150792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1160792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
11778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
11878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
11978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Type of logical devices hosted in the system. Stored in the unmodifiable list.
1210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final List<Integer> mLocalDevices;
12278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
12378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
12478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
1254893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
12678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
12778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
12878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
1294893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
13078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
13178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
13278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1336d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of listeners registered by callers that want to get notified of
1346d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // device status events.
1354893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1366d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
1376d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1386d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    // List of records for device event listener to handle the the caller killed in action.
1394893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    @GuardedBy("mLock")
1406d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
1416d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            new ArrayList<>();
1426d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
1439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1449c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private IHdmiInputChangeListener mInputChangeListener;
1459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
1469c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    @GuardedBy("mLock")
1479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private InputChangeListenerRecord mInputChangeListenerRecord;
1489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
14992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // Set to true while HDMI control is enabled. If set to false, HDMI-CEC/MHL protocol
15092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    // handling will be disabled and no request will be handled.
15192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    @GuardedBy("mLock")
15292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    private boolean mHdmiControlEnabled;
15392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
1544d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // Set to true while the service is in normal mode. While set to false, no input change is
1554d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // allowed. Used for situations where input change can confuse users such as channel auto-scan,
1564d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    // system upgrade, etc., a.k.a. "prohibit mode".
1574d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    @GuardedBy("mLock")
1584d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    private boolean mProhibitMode;
1594d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
160ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of listeners registered by callers that want to get notified of
161ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // system audio mode changes.
162ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<IHdmiSystemAudioModeChangeListener>
163ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners = new ArrayList<>();
164ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    // List of records for system audio mode change to handle the the caller killed in action.
165ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final ArrayList<SystemAudioModeChangeListenerRecord>
166ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
167ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1684893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    // Handler used to run a task in service thread.
1690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private final Handler mHandler = new Handler();
1700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1710792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1720792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1730792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1740792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1750792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1760792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1770340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // HDMI port information. Stored in the unmodifiable list to keep the static information
1780340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // from being modified.
1790340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> mPortInfo;
1800340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
18138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
18238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
18338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
18438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private int mPowerStatus = HdmiCec.POWER_STATUS_STANDBY;
18538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
18638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
18738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private boolean mStandbyMessageReceived = false;
18838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
1890792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1900792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
1910340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
1920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
1930792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1940792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1950792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1972f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
19838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mPowerStatus = HdmiCec.POWER_STATUS_TRANSIENT_TO_ON;
199e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
2008b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
2013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
20238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mCecController.setOption(HdmiCec.OPTION_CEC_SERVICE_CONTROL, HdmiCec.DISABLED);
2033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
204a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
2050792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
2060792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2070792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
208e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
2090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
2100792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
2110792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
2120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        mPortInfo = initPortInfo();
2138692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
21463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
21538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // Register broadcast receiver for power state change.
21638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null || mMhlController != null) {
21738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            IntentFilter filter = new IntentFilter();
21838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_OFF);
21938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            filter.addAction(Intent.ACTION_SCREEN_ON);
22038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            getContext().registerReceiver(mPowerStateReceiver, filter);
22138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
22238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
22363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
22463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
22592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        mHdmiControlEnabled = true;
2264d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        // TODO: Get control flag from persistent storage
2274d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        mProhibitMode = false;
2280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
229e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
230a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private void initializeLocalDevices(final List<Integer> deviceTypes) {
232a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
2343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
2353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
23613c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim        mCecController.clearLogicalAddress();
2373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
2383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
2393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
2403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
2413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
2423ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
2433ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
2443ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
2453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
2463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
2473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
2483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
2493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
2503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
2513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
2523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
2533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
2543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2554893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    // Address allocation completed for all devices. Notify each device.
2560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    if (deviceTypes.size() == finished.size()) {
25738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        if (mPowerStatus == HdmiCec.POWER_STATUS_TRANSIENT_TO_ON) {
25838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                            mPowerStatus = HdmiCec.POWER_STATUS_ON;
25938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        }
2603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
2613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
2623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
2633ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
2643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
267a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
269a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
2713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
2723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
27313c030e828a90fcfc57b52024b72326757cec583Jinsuk Kim            device.handleAddressAllocated(address);
2743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2770340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
2780340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    // keep them in one place.
279a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
2800340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    private List<HdmiPortInfo> initPortInfo() {
281a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
2820340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] cecPortInfo = null;
2830340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2840340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // CEC HAL provides majority of the info while MHL does only MHL support flag for
2850340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // each port. Return empty array if CEC HAL didn't provide the info.
2860340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mCecController != null) {
2870340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            cecPortInfo = mCecController.getPortInfos();
2880340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2890340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (cecPortInfo == null) {
2900340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return Collections.emptyList();
2910340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2920340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2930340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
2940340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        if (mMhlController != null) {
2950340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // TODO: Implement plumbing logic to get MHL port information.
2960340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mhlPortInfo = mMhlController.getPortInfos();
2970340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
2980340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2990340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // Use the id (port number) to find the matched info between CEC and MHL to combine them
3000340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
3010340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
3020340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int i = 0; i < cecPortInfo.length; ++i) {
3030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            HdmiPortInfo cec = cecPortInfo[i];
3040340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int id = cec.getId();
3050340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            boolean mhlInfoFound = false;
3060340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (HdmiPortInfo mhl : mhlPortInfo) {
3070340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                if (id == mhl.getId()) {
3080340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
3090340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                            cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
3100340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    mhlInfoFound = true;
3110340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                    break;
3120340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                }
3130340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3140340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (!mhlInfoFound) {
3150340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                result.add(cec);
3160340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3170340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(result);
3200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
3220340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
3230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Returns HDMI port information for the given port id.
3240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
3250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param portId HDMI port id
3260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link HdmiPortInfo} for the given port
3270340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
3280340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    HdmiPortInfo getPortInfo(int portId) {
3290340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // mPortInfo is an unmodifiable list and the only reference to its inner list.
3300340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        // No lock is necessary.
3310340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
3320340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            if (portId == info.getId()) {
3330340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                return info;
3340340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            }
3350340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
3360340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return null;
3370340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
3380340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
339e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
340401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the routing path (physical address) of the HDMI port for the given
341401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * port id.
342401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
343401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int portIdToPath(int portId) {
344401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        HdmiPortInfo portInfo = getPortInfo(portId);
345401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        if (portInfo == null) {
346401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            Slog.e(TAG, "Cannot find the port info: " + portId);
34760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            return HdmiConstants.INVALID_PHYSICAL_ADDRESS;
348401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
349401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        return portInfo.getAddress();
350401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
351401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
352401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
353401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * Returns the id of HDMI port located at the top of the hierarchy of
354401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
355401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * the port id to be returned is the ID associated with the port address
356401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
357401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim     */
358401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    int pathToPortId(int path) {
359401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        int portAddress = path & HdmiConstants.ROUTING_PATH_TOP_MASK;
360401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        for (HdmiPortInfo info : mPortInfo) {
361401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            if (portAddress == info.getAddress()) {
362401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim                return info.getId();
363401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim            }
364401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim        }
36560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return HdmiConstants.INVALID_PORT_ID;
366401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    }
367401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim
368401e3de791c0e2a4348361fbd560da9530156e22Jinsuk Kim    /**
369e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
370e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
371e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
372e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
373e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
374e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
375e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
376e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
377e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
378e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
379e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
380e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
381e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
382e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
383e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
38467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
385e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
386c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
387c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
3883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
3893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
3913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
3923ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
3933ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
3943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
3953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
3963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
3973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
3983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
3993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
401a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
402a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
4030340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        assertRunOnServiceThread();
40479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDeviceTv tv = tv();
40579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        if (tv == null) {
40679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            return null;
40779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
40879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return tv.getDeviceInfo(logicalAddress);
409a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim    }
410a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
4113ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
412092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
413092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
414092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
415092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
416092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
417092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
418092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
41960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     * Whether a device of the specified physical address is connected to ARC enabled port.
42060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang     */
42160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    boolean isConnectedToArcPort(int physicalAddress) {
42260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        for (HdmiPortInfo portInfo : mPortInfo) {
42360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            if (hasSameTopPort(portInfo.getAddress(), physicalAddress)
42460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                    && portInfo.isArcSupported()) {
42560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                return true;
42660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
42760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
42860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return false;
42960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
43060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
43179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void runOnServiceThread(Runnable runnable) {
43267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
43367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
43467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
43563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
43663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
43763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
43863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
43963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
44063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
44163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
44263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
44363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
44463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
44567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
446c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
447c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
448c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
449d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
450c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
451a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
452d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
453a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
454d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
455d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
456d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
457a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
458d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
459a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
460d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
461c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
462c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
463a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
464a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
465a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
466092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
467092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
468092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
46979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioReturnChannel(boolean enabled) {
47079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        mCecController.setAudioReturnChannel(enabled);
47160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
47260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
473a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
474092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
475a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
476092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
47779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            if (device.dispatchMessage(message)
47879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
479092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
480092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
481092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
48260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
4833a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        if (message.getDestination() != HdmiCec.ADDR_BROADCAST) {
4843a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang            Slog.w(TAG, "Unhandled cec command:" + message);
4853a959fca91bce393cc1ee79aa2985bb06542016eJungshik Jang        }
486092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
487a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
488a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
48967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
49067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
49167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
4928b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
49367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
49467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
495a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
49667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
49760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
49879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
49979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            device.onHotplug(portNo, connected);
50060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
50160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        announceHotplugEvent(portNo, connected);
50267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
50367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
50402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
50502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
50602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
50702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
50802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
5091de514256fd3015cf45256f3198ab5472024af9bJungshik Jang     * @param sourceAddress a logical address of source device where sends polling message
5100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
51102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
5120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
51302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
514a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
5151de514256fd3015cf45256f3198ab5472024af9bJungshik Jang    void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
5161de514256fd3015cf45256f3198ab5472024af9bJungshik Jang            int retryCount) {
517a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang        assertRunOnServiceThread();
5181de514256fd3015cf45256f3198ab5472024af9bJungshik Jang        mCecController.pollDevices(callback, sourceAddress, checkPollStrategy(pickStrategy),
5191de514256fd3015cf45256f3198ab5472024af9bJungshik Jang                retryCount);
5200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
5210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
5220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
5233ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int strategy = pickStrategy & HdmiConstants.POLL_STRATEGY_MASK;
5240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
5250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
5260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
5273ecdd832c77483c909fbf90d17d0e6d97ca365eeJungshik Jang        int iterationStrategy = pickStrategy & HdmiConstants.POLL_ITERATION_STRATEGY_MASK;
5280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
5290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
5300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
5310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
53202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
53302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
53460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    List<HdmiCecLocalDevice> getAllLocalDevices() {
53560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        assertRunOnServiceThread();
53660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return mCecController.getLocalDeviceList();
53760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
53860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
53979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    Object getServiceLock() {
54079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return mLock;
54179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
54279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
54379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    void setAudioStatus(boolean mute, int volume) {
54479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        // TODO: Hook up with AudioManager.
5453ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5463ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
547ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    void announceSystemAudioModeChange(boolean enabled) {
548ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
549ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            invokeSystemAudioModeChange(listener, enabled);
550ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
551ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
552ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
5533ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
55442c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        // TODO: find better name instead of model name.
55542c9800f4f3acef10d19dca39e8b739546407c04Jungshik Jang        String displayName = Build.MODEL;
5563ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
5573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
5583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
5593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
56078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
56178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
56278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
56378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
56478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
56678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
56778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
56878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
57078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
5786d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private final class DeviceEventListenerRecord implements IBinder.DeathRecipient {
5796d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        private final IHdmiDeviceEventListener mListener;
5806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5816d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public DeviceEventListenerRecord(IHdmiDeviceEventListener listener) {
5826d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            mListener = listener;
5836d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5846d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
5856d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
586ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
5876d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            synchronized (mLock) {
5886d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListenerRecords.remove(this);
5896d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                mDeviceEventListeners.remove(mListener);
5906d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
5916d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
5926d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
5936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
594ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
59538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        private final IHdmiSystemAudioModeChangeListener mListener;
596ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
597ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
598ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mListener = listener;
599ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
600ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
601ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
602ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void binderDied() {
603ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            synchronized (mLock) {
604ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListenerRecords.remove(this);
605ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                mSystemAudioModeChangeListeners.remove(mListener);
606ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
607ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
608ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
609ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
61078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
61178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
61278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
61378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
61478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
61578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
61678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
61778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6180340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            // mLocalDevices is an unmodifiable list - no lock necesary.
6190340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            int[] localDevices = new int[mLocalDevices.size()];
6200340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            for (int i = 0; i < localDevices.length; ++i) {
6210340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim                localDevices[i] = mLocalDevices.get(i);
62278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
6230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            return localDevices;
62478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
62578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
62678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
627a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
628a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            enforceAccessPermission();
629a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            runOnServiceThread(new Runnable() {
630a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                @Override
631a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                public void run() {
63279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
633a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    if (tv == null) {
634a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
635a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
636a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                        return;
637a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    }
638a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                    tv.deviceSelect(logicalAddress, callback);
639a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim                }
640a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim            });
641a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        }
642a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim
643a6ce7708d6124224399241503fadcafe0c4684d4Jinsuk Kim        @Override
644a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void portSelect(final int portId, final IHdmiControlCallback callback) {
645a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
646a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
647a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
648a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
649a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
650a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
651a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
652a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
653a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
654a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
6558333571bd5e0a08773a1679964f8d96227af3356Jinsuk Kim                    tv.doManualPortSwitching(portId, callback);
656a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
657a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
658a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
659a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
660a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
661a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
662a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            enforceAccessPermission();
663a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            runOnServiceThread(new Runnable() {
664a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                @Override
665a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                public void run() {
666a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    // TODO: sendKeyEvent is for TV device only for now. Allow other
667a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    //       local devices of different types to use this as well.
668a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
669a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    if (tv == null) {
670a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        Slog.w(TAG, "Local tv device not available");
671a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                        return;
672a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    }
673a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                    tv.sendKeyEvent(keyCode, isPressed);
674a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim                }
675a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim            });
676a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        }
677a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim
678a062a9339add79a84862a34e363e3e454a6ec435Jinsuk Kim        @Override
6797fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
68078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
6857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
68778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
68878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
68978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
69178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
6967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
69878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
69978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
70078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
70278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
7077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7087fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
70978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
71078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
71178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
7127fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
71378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
7147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
7157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
7167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
7177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
7187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
7197fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
72078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
7216d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
7226d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
7236d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
7246d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
7256d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            runOnServiceThread(new Runnable() {
7266d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                @Override
727ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
7286d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                    HdmiControlService.this.addDeviceEventListener(listener);
7296d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
7306d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            });
7316d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
7326d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
7336d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        @Override
7346d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        public List<HdmiPortInfo> getPortInfo() {
7356d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            enforceAccessPermission();
7366d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            return mPortInfo;
7376d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
738ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
739ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
740ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean canChangeSystemAudioMode() {
741ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
742ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
743ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
744ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
745ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
746e9cf1583c74fd03977c1ecb14520663710f14439Jungshik Jang            return tv.hasSystemAudioDevice();
747ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
748ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
749ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
750ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public boolean getSystemAudioMode() {
751ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
752ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiCecLocalDeviceTv tv = tv();
753ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            if (tv == null) {
754ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                return false;
755ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
756ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return tv.getSystemAudioMode();
757ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
758ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
759ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
760ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
761ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
762ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            runOnServiceThread(new Runnable() {
763ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                @Override
764ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                public void run() {
765ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
766ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    if (tv == null) {
767ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
768ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
769ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                        return;
770ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    }
771ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    tv.changeSystemAudioMode(enabled, callback);
772ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
773ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            });
774ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
775ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
776ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
777ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void addSystemAudioModeChangeListener(
778ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
779ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
780ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
781ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
782ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
783ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        @Override
784ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        public void removeSystemAudioModeChangeListener(
785ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                final IHdmiSystemAudioModeChangeListener listener) {
786ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            enforceAccessPermission();
787ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
788ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
78992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
79092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        @Override
7919c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
7929c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
7939c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiControlService.this.setInputChangeListener(listener);
7949c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
7959c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
7969c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
7979c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public List<HdmiCecDeviceInfo> getInputDevices() {
7989c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            enforceAccessPermission();
7999c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // No need to hold the lock for obtaining TV device as the local device instance
8009c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            // is preserved while the HDMI control is enabled.
8019c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            HdmiCecLocalDeviceTv tv = tv();
8029c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (tv == null) {
8039c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return Collections.emptyList();
8049c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
8059c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            return tv.getSafeExternalInputs();
8069c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
8079c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
8089c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
809160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setControlEnabled(final boolean enabled) {
81092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            enforceAccessPermission();
81192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            synchronized (mLock) {
81292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                mHdmiControlEnabled = enabled;
81392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
81492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            // TODO: Stop the running actions when disabled, and start
81592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            //       address allocation/device discovery when enabled.
81692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (!enabled) {
81792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return;
81892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
81992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            runOnServiceThread(new Runnable() {
82092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                @Override
82192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                public void run() {
82292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    HdmiCecLocalDeviceTv tv = tv();
82392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    if (tv == null) {
82492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                        return;
82592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                    }
826160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    int value = enabled ? HdmiCec.ENABLED : HdmiCec.DISABLED;
827160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(HdmiCec.OPTION_CEC_ENABLE, value);
828160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
829160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(HdmiCec.OPTION_MHL_ENABLE, value);
830160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
8315344cd98e69f92e70d52969b1851c9d8f9e81853Jinsuk Kim                    tv.launchRoutingControl(false);
83292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                }
83392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            });
83492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
835a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang
836a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        @Override
83741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
83841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                final int maxIndex) {
83941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
84041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
84141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
84241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
84341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
84441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
84541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
84641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
84741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
84841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeVolume(oldIndex, newIndex - oldIndex, maxIndex);
84941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
85041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
85141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
85241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
85341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
85441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        public void setSystemAudioMute(final boolean mute) {
85541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            enforceAccessPermission();
85641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            runOnServiceThread(new Runnable() {
85741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                @Override
85841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                public void run() {
85941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
86041d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    if (tv == null) {
86141d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        Slog.w(TAG, "Local tv device not available");
86241d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                        return;
86341d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    }
86441d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                    tv.changeMute(mute);
86541d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang                }
86641d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang            });
86741d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        }
86841d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang
86941d974631c5f525da49c88d34cecedd5a4cfeda8Jungshik Jang        @Override
870a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        public void setArcMode(final boolean enabled) {
871a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            enforceAccessPermission();
872a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            runOnServiceThread(new Runnable() {
873a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                @Override
874a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                public void run() {
875a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    HdmiCecLocalDeviceTv tv = tv();
876a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    if (tv == null) {
87738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                        Slog.w(TAG, "Local tv device not available to change arc mode.");
878a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                        return;
879a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                    }
880a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang                }
881a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang            });
882a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang        }
883160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
884160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        @Override
885160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        public void setOption(final int key, final int value) {
8864d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
887160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            if (!isTvDevice()) {
888160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                return;
889160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
890160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            switch (key) {
891160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                case HdmiCec.OPTION_CEC_AUTO_WAKEUP:
892160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    mCecController.setOption(key, value);
893160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
894160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                case HdmiCec.OPTION_CEC_AUTO_DEVICE_OFF:
895160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    // No need to pass this option to HAL.
896160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    tv().setAutoDeviceOff(value == HdmiCec.ENABLED);
897160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
898160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                case HdmiCec.OPTION_MHL_INPUT_SWITCHING:  // Fall through
899160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                case HdmiCec.OPTION_MHL_POWER_CHARGE:
900160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    if (mMhlController != null) {
901160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                        mMhlController.setOption(key, value);
902160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    }
903160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim                    break;
904160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            }
905160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
906160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim
907160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        private boolean isTvDevice() {
908160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim            return tv() != null;
909160a6e5b99de15ce755e2e5521dce32d81ab180aJinsuk Kim        }
9104d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
9114d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        @Override
9124d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        public void setProhibitMode(final boolean enabled) {
9134d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            enforceAccessPermission();
9144d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            if (!isTvDevice()) {
9154d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim                return;
9164d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            }
9174d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            HdmiControlService.this.setProhibitMode(enabled);
9184d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
91978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
92078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
921a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
92279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void oneTouchPlay(final IHdmiControlCallback callback) {
92379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
92479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
9257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
9267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
9277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
9287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
9297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
93079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.oneTouchPlay(callback);
93178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
93278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
933a5b7414970c85217e88015e78ecbc5ba093dead3Jungshik Jang    @ServiceThreadOnly
93479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private void queryDisplayStatus(final IHdmiControlCallback callback) {
93579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        assertRunOnServiceThread();
93679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecLocalDevicePlayback source = playback();
9377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
9387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
9397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
9407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
9417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
94279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        source.queryDisplayStatus(callback);
94378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
94478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
94578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
94678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
94778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
94878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
94978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
95078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
95178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
95278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
95378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
95478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
95578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
95678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
95778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
95878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
95978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
96078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
96178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
96278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
96378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
96478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
96578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
96678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
96778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
96878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
96978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
97078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
9717fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
9726d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
9734893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
9744893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        try {
9754893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
9764893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        } catch (RemoteException e) {
9774893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            Slog.w(TAG, "Listener already died");
9784893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            return;
9794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
9806d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        synchronized (mLock) {
9814893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListeners.add(listener);
9824893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            mDeviceEventListenerRecords.add(record);
9834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        }
9844893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    }
9854893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim
9864893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
9874893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        synchronized (mLock) {
9884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
9894893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                try {
9904893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    listener.onStatusChanged(device, activated);
9914893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                } catch (RemoteException e) {
9924893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                    Slog.e(TAG, "Failed to report device event:" + e);
9936d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim                }
9946d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim            }
9956d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim        }
9966d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim    }
9976d97f5b91c6c82f28a2a3a5d3b922f0e5844e733Jinsuk Kim
998ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
999ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
1000ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                listener);
1001ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1002ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.asBinder().linkToDeath(record, 0);
1003ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1004ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.w(TAG, "Listener already died");
1005ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            return;
1006ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1007ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1008ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.add(listener);
1009ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListenerRecords.add(record);
1010ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1011ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1012ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
1013ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
1014ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        synchronized (mLock) {
1015ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            for (SystemAudioModeChangeListenerRecord record :
1016ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords) {
1017ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                if (record.mListener.asBinder() == listener) {
1018ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    listener.asBinder().unlinkToDeath(record, 0);
1019ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    mSystemAudioModeChangeListenerRecords.remove(record);
1020ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                    break;
1021ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang                }
1022ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            }
1023ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            mSystemAudioModeChangeListeners.remove(listener);
1024ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1025ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1026ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
10279c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private final class InputChangeListenerRecord implements IBinder.DeathRecipient {
10289c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        @Override
10299c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        public void binderDied() {
10309c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            synchronized (mLock) {
10319c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                mInputChangeListener = null;
10329c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
10339c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
10349c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
10359c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
10369c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    private void setInputChangeListener(IHdmiInputChangeListener listener) {
10379c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
10389c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListenerRecord = new InputChangeListenerRecord();
10399c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            try {
10409c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                listener.asBinder().linkToDeath(mInputChangeListenerRecord, 0);
10419c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            } catch (RemoteException e) {
10429c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                Slog.w(TAG, "Listener already died");
10439c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                return;
10449c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
10459c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            mInputChangeListener = listener;
10469c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
10479c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
10489c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
10499c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    void invokeInputChangeListener(int activeAddress) {
10509c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        synchronized (mLock) {
10519c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            if (mInputChangeListener != null) {
10529c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                HdmiCecDeviceInfo activeSource = getDeviceInfo(activeAddress);
10539c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                try {
10549c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    mInputChangeListener.onChanged(activeSource);
10559c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                } catch (RemoteException e) {
10569c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                    Slog.w(TAG, "Exception thrown by IHdmiInputChangeListener: " + e);
10579c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim                }
10589c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim            }
10599c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim        }
10609c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim    }
10619c37e1f53ea4734bfe5ae156dc5399ce5f2c7cccJinsuk Kim
10627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
10637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
10647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
10657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
10667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
10677fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
10687fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
106963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1070ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
1071ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            boolean enabled) {
1072ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        try {
1073ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            listener.onStatusChanged(enabled);
1074ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        } catch (RemoteException e) {
1075ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang            Slog.e(TAG, "Invoking callback failed:" + e);
1076ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang        }
1077ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang    }
1078ea67c183fe5511ad99aeaae1a32b5245bd020e36Jungshik Jang
10794893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void announceHotplugEvent(int portId, boolean connected) {
10804893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
108160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        synchronized (mLock) {
108260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
10834893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim                invokeHotplugEventListenerLocked(listener, event);
108460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            }
108560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
108660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
108760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
10884893c7efde52411ad051ef5c20251439f4098eacJinsuk Kim    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
108960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            HdmiHotplugEvent event) {
109060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        try {
109160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            listener.onReceived(event);
109260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        } catch (RemoteException e) {
109360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang            Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e);
109460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        }
1095e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
1096e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
109760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    private static boolean hasSameTopPort(int path1, int path2) {
109860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang        return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK)
109960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang                == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK);
110060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
110160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang
110279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDeviceTv tv() {
110379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
110479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
110579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
110679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    private HdmiCecLocalDevicePlayback playback() {
110779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
110860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang    }
1109a858d221ff86c497e745222ea15bab141e337636Jungshik Jang
1110a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    AudioManager getAudioManager() {
1111a858d221ff86c497e745222ea15bab141e337636Jungshik Jang        return (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1112a858d221ff86c497e745222ea15bab141e337636Jungshik Jang    }
111392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
111492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    boolean isControlEnabled() {
111592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        synchronized (mLock) {
111692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return mHdmiControlEnabled;
111792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
111892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
111938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
112038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    int getPowerStatus() {
112138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus;
112238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
112338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
112438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerOnOrTransient() {
112538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus == HdmiCec.POWER_STATUS_ON
112638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                || mPowerStatus == HdmiCec.POWER_STATUS_TRANSIENT_TO_ON;
112738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
112838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
112938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandbyOrTransient() {
113038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus == HdmiCec.POWER_STATUS_STANDBY
113138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo                || mPowerStatus == HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY;
113238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
113338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
113438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    boolean isPowerStandby() {
113538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        return mPowerStatus == HdmiCec.POWER_STATUS_STANDBY;
113638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
113738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
113838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
113938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void wakeUp() {
114038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
114138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
114238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.wakeUp(SystemClock.uptimeMillis());
114338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets
114438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onWakeUp().
114538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
114638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
114738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
114838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void standby() {
114938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
115038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = true;
115138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
115238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        pm.goToSleep(SystemClock.uptimeMillis());
115338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets
115438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // the intent, the sequence will continue at onStandby().
115538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
115638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
115738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
115838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onWakeUp() {
115938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
116038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mPowerStatus = HdmiCec.POWER_STATUS_TRANSIENT_TO_ON;
116138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mCecController != null) {
116238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            mCecController.setOption(HdmiCec.OPTION_CEC_SERVICE_CONTROL, HdmiCec.ENABLED);
116338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            initializeLocalDevices(mLocalDevices);
116438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        } else {
116538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            Slog.i(TAG, "Device does not support HDMI-CEC.");
116638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
116738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        // TODO: Initialize MHL local devices.
116838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
116938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
117038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
117138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    private void onStandby() {
117238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
117338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mPowerStatus = HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY;
117438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
117538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            device.onTransitionToStandby(mStandbyMessageReceived);
117638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
117738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
117838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo
117938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    /**
118038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * Called when there are the outstanding actions in the local devices.
118138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * This callback is used to wait for when the action queue is empty
118238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     * during the power state transition to standby.
118338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo     */
118438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    @ServiceThreadOnly
118538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    void onPendingActionsCleared() {
118638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        assertRunOnServiceThread();
118738db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        if (mPowerStatus != HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY) {
118838db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            return;
118938db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
119038db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mPowerStatus = HdmiCec.POWER_STATUS_STANDBY;
119138db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
119238db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo            device.onStandBy(mStandbyMessageReceived);
119338db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        }
119438db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mStandbyMessageReceived = false;
119538db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo        mCecController.setOption(HdmiCec.OPTION_CEC_SERVICE_CONTROL, HdmiCec.DISABLED);
119638db629d897e9d7c8e31ce0a7e985981e3e12996Yuncheol Heo    }
11974d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
11984d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    boolean isProhibitMode() {
11994d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
12004d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            return mProhibitMode;
12014d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
12024d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
12034d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim
12044d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    void setProhibitMode(boolean enabled) {
12054d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        synchronized (mLock) {
12064d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim            mProhibitMode = enabled;
12074d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim        }
12084d43d93743222311c6377d4904c19ccb93699d3bJinsuk Kim    }
12090792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
1210