HdmiControlService.java revision 092b445ef898e3c1e5b2918b554480940f0f5a28
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;
200792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.content.Context;
21a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport android.hardware.hdmi.HdmiCec;
22c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecDeviceInfo;
23c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kimimport android.hardware.hdmi.HdmiCecMessage;
24d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlCallback;
25d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiControlService;
26d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jangimport android.hardware.hdmi.IHdmiHotplugEventListener;
2767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport android.os.Handler;
280792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.os.HandlerThread;
2978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.IBinder;
30e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jangimport android.os.Looper;
3178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport android.os.RemoteException;
320792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport android.util.Slog;
333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport android.util.SparseArray;
348b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jangimport android.util.SparseIntArray;
3578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
3678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport com.android.internal.annotations.GuardedBy;
370792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangimport com.android.server.SystemService;
38cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jangimport com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
4178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kimimport java.util.ArrayList;
4267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport java.util.Iterator;
4367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jangimport java.util.LinkedList;
4402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jangimport java.util.List;
45a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jangimport java.util.Locale;
46a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang/**
480792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * Provides a service for sending and processing HDMI control messages,
490792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang * HDMI-CEC and MHL control command, and providing the information on both standard.
500792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang */
510792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jangpublic final class HdmiControlService extends SystemService {
520792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private static final String TAG = "HdmiControlService";
530792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
5478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // TODO: Rename the permission to HDMI_CONTROL.
5578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private static final String PERMISSION = "android.permission.HDMI_CEC";
5678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
57ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_SUCCESS = 0;
58ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_NAK = -1;
59ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo    static final int SEND_RESULT_FAILURE = -2;
60ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo
610f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_MASK = 0x3;  // first and second bit.
620f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
630f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
640f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
650f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_STRATEGY_MASK = 0x30000;  // first and second bit.
660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_IN_ORDER = 0x10000;
670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
69d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    /**
70d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * Interface to report send result.
71d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     */
72d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    interface SendMessageCallback {
73d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        /**
74d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         * Called when {@link HdmiControlService#sendCecCommand} is completed.
75d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         *
76ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @param error result of send request.
77ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_SUCCESS}
78ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_NAK}
79ece603b7955938d6001c376f351ca0a2219330acYuncheol Heo         * @see {@link #SEND_RESULT_FAILURE}
80d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang         */
81d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        void onSendCompleted(int error);
82d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
83d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
8402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
8502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Interface to get a list of available logical devices.
8602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
8702bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    interface DevicePollingCallback {
8802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        /**
8902bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * Called when device polling is finished.
9002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         *
9102bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         * @param ackedAddress a list of logical addresses of available devices
9202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang         */
9302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang        void onPollingFinished(List<Integer> ackedAddress);
9402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
9502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
960792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // A thread to handle synchronous IO of CEC and MHL control service.
970792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
980792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    // and sparse call it shares a thread to handle IO operations.
990792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
1000792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
10167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // A collection of FeatureAction.
10267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Note that access to this collection should happen in service thread.
10367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private final LinkedList<FeatureAction> mActions = new LinkedList<>();
10467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
10578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Used to synchronize the access to the service.
10678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final Object mLock = new Object();
10778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
10878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Type of logical devices hosted in the system.
10978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    @GuardedBy("mLock")
11078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final int[] mLocalDevices;
11178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
11278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of listeners registered by callers that want to get notified of
11378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // hotplug events.
11478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
11578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
11678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // List of records for hotplug event listener to handle the the caller killed in action.
11778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
11878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            new ArrayList<>();
11978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
120e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
121e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
1220792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1230792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiCecController mCecController;
1240792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1250792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Nullable
1260792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    private HdmiMhlController mMhlController;
1270792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    @GuardedBy("mLock")
12967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Whether ARC is "enabled" or not.
13067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // TODO: it may need to hold lock if it's accessed from others.
13167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private boolean mArcStatusEnabled = false;
13267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
1330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    @GuardedBy("mLock")
13463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    // Whether SystemAudioMode is "On" or not.
13563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private boolean mSystemAudioMode;
13663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
13767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Handler running on service thread. It's used to run a task in service thread.
13863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private final Handler mHandler = new Handler();
13967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
1400792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public HdmiControlService(Context context) {
1410792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        super(context);
14278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        mLocalDevices = getContext().getResources().getIntArray(
14378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                com.android.internal.R.array.config_hdmiCecLogicalDeviceType);
1440792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
1450792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
1460792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    @Override
1470792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    public void onStart() {
1482f51aec689226e259d08bf04d838251f249572e3Jungshik Jang        mIoThread.start();
149e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mCecController = HdmiCecController.create(this);
1508b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
1513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        if (mCecController != null) {
1523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            initializeLocalDevices(mLocalDevices);
153a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8fJinsuk Kim        } else {
1540792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support HDMI-CEC.");
1550792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
1560792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang
157e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        mMhlController = HdmiMhlController.create(this);
1580792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        if (mMhlController == null) {
1590792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang            Slog.i(TAG, "Device does not support MHL-control.");
1600792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang        }
16178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
1628692fc68a413c7aca75f094bb02bcbabcc277c73Jinsuk Kim        publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
16363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
16463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
16563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
1660792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang    }
167e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
1683ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void initializeLocalDevices(final int[] deviceTypes) {
1693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // A container for [Logical Address, Local device info].
1703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
1713ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        final SparseIntArray finished = new SparseIntArray();
1723ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int type : deviceTypes) {
1733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
1743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            localDevice.init();
1753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            mCecController.allocateLogicalAddress(type,
1763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    localDevice.getPreferredAddress(), new AllocateAddressCallback() {
1773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                @Override
1783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                public void onAllocated(int deviceType, int logicalAddress) {
1793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
1803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
1813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    } else {
1823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
1833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        localDevice.setDeviceInfo(deviceInfo);
1843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLocalDevice(deviceType, localDevice);
1853ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        mCecController.addLogicalAddress(logicalAddress);
1863ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        devices.append(logicalAddress, localDevice);
1873ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1883ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    finished.append(deviceType, logicalAddress);
1893ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
1903ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    // Once finish address allocation for all devices, notify
1913ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    // it to each device.
1923ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    if (deviceTypes.length == finished.size()) {
1933ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        notifyAddressAllocated(devices);
1943ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
1953ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                }
1963ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            });
1973ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
1983ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
1993ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2003ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
2013ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        for (int i = 0; i < devices.size(); ++i) {
2023ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            int address = devices.keyAt(i);
2033ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            HdmiCecLocalDevice device = devices.valueAt(i);
2043ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang            device.onAddressAllocated(address);
2053ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        }
2063ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2073ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
208e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
209e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} for IO operation.
210e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
211e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
212e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
213e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getIoLooper() {
214e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang        return mIoThread.getLooper();
215e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
216e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang
217e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    /**
218e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
219e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * for tasks that are running on main service thread.
220e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     *
221e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     * <p>Declared as package-private.
222e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang     */
223e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    Looper getServiceLooper() {
22467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        return mHandler.getLooper();
225e9c77c88ea34a66f83a94f960547275c0ff6bd07Jungshik Jang    }
226c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
227c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
2283ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns physical address of the device.
2293ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
2303ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getPhysicalAddress() {
2313ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getPhysicalAddress();
2323ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2333ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2343ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
2353ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Returns vendor id of CEC service.
2363ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
2373ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    int getVendorId() {
2383ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return mCecController.getVendorId();
2393ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
2403ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
2413ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
242092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Returns version of CEC.
243092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
244092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    int getCecVersion() {
245092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return mCecController.getVersion();
246092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
247092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
248092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
2490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * Returns a list of {@link HdmiCecDeviceInfo}.
2500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     *
2510f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param includeLocalDevice whether to include local devices
2520f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     */
2530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
2540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        assertRunOnServiceThread();
2550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return mCecController.getDeviceInfoList(includeLocalDevice);
2560f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2570f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    /**
25967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Add and start a new {@link FeatureAction} to the action queue.
260c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
26167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to add and start
262c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
26367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void addAndStartAction(final FeatureAction action) {
26467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: may need to check the number of stale actions.
26567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        runOnServiceThread(new Runnable() {
26667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            @Override
26767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            public void run() {
26867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                mActions.add(action);
26967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                action.start();
27067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
27167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        });
272c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
273c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
2740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void setSystemAudioMode(boolean on) {
2750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        synchronized (mLock) {
2760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            mSystemAudioMode = on;
2770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
2780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    boolean getSystemAudioMode() {
2810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        synchronized (mLock) {
2820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            return mSystemAudioMode;
2830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
2840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
2850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
2867fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    // See if we have an action of a given type in progress.
287092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
2887fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        for (FeatureAction action : mActions) {
2897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            if (action.getClass().equals(clazz)) {
2907fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                return true;
2917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            }
2927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
2937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        return false;
2947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
2957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
296c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
297c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Remove the given {@link FeatureAction} object from the action queue.
298c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
29967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param action {@link FeatureAction} to remove
300c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
30167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void removeAction(final FeatureAction action) {
30263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        assertRunOnServiceThread();
30363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mActions.remove(action);
30467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
30567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
30667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    // Remove all actions matched with the given Class type.
30767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
30863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        removeActionExcept(clazz, null);
30963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
31063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
31163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    // Remove all actions matched with the given Class type besides |exception|.
31263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
31363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            final FeatureAction exception) {
31463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        assertRunOnServiceThread();
31563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        Iterator<FeatureAction> iter = mActions.iterator();
31663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        while (iter.hasNext()) {
31763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            FeatureAction action = iter.next();
31863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            if (action != exception && action.getClass().equals(clazz)) {
31963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                action.clear();
32063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                mActions.remove(action);
32167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            }
32263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
32367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
32467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
32567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void runOnServiceThread(Runnable runnable) {
32667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        mHandler.post(runnable);
32767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
32867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
32963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void runOnServiceThreadAtFrontOfQueue(Runnable runnable) {
33063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        mHandler.postAtFrontOfQueue(runnable);
33163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
33263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
33363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void assertRunOnServiceThread() {
33463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (Looper.myLooper() != mHandler.getLooper()) {
33563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalStateException("Should run on service thread.");
33663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
33763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
33863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
33967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
34067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Change ARC status into the given {@code enabled} status.
34167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
34267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @return {@code true} if ARC was in "Enabled" status
34367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
34467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    boolean setArcStatus(boolean enabled) {
345092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        assertRunOnServiceThread();
3460f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        synchronized (mLock) {
3470f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            boolean oldStatus = mArcStatusEnabled;
3480f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            // 1. Enable/disable ARC circuit.
349092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            mCecController.setAudioReturnChannel(enabled);
350092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
351092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            // TODO: notify arc mode change to AudioManager.
35267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
3530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            // 2. Update arc status;
3540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            mArcStatusEnabled = enabled;
3550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            return oldStatus;
3560f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
357c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
358c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
359c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    /**
360c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * Transmit a CEC command to CEC bus.
361c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     *
362c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     * @param command CEC command to send out
363d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang     * @param callback interface used to the result of send command
364c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim     */
365d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
366d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, callback);
367d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    }
368d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang
369d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang    void sendCecCommand(HdmiCecMessage command) {
370d643f764f72efc1e7aa67392bf9ac40720ae14c3Jungshik Jang        mCecController.sendCommand(command, null);
371c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim    }
372c70d2295dd3fb87ce8c81c704688d1ad05043b4dJinsuk Kim
373a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    boolean handleCecCommand(HdmiCecMessage message) {
374e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        // Cache incoming message. Note that it caches only white-listed one.
375e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        mCecMessageCache.cacheMessage(message);
376e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
377a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // Commands that queries system information replies directly instead
378a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        // of creating FeatureAction because they are state-less.
379092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // TODO: move the leftover message to local device.
380a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        switch (message.getOpcode()) {
38167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_INITIATE_ARC:
38267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleInitiateArc(message);
38367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
38467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang            case HdmiCec.MESSAGE_TERMINATE_ARC:
38567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                handleTerminateArc(message);
38667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                return true;
38763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
38863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                handleSetSystemAudioMode(message);
38963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                return true;
39063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
39163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                handleSystemAudioModeStatus(message);
39263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                return true;
393a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang            default:
394092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                if (dispatchMessageToAction(message)) {
395092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                    return true;
396092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                }
397092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                break;
398a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang        }
399092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
400092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return dispatchMessageToLocalDevice(message);
401092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
402092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
403092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
404092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
405092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            if (device.dispatchMessage(message)) {
406092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang                return true;
407092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang            }
408092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        }
409092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return false;
410a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang    }
411a1fa91fe263c483cf13066e2847a440de2cd52a5Jungshik Jang
41267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    /**
41367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * Called when a new hotplug event is issued.
41467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     *
4158b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang     * @param portNo hdmi port number where hot plug event issued.
41667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     * @param connected whether to be plugged in or not
41767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang     */
41867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    void onHotplug(int portNo, boolean connected) {
41967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: Start "RequestArcInitiationAction" if ARC port.
42067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
42167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
42202bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    /**
42302bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * Poll all remote devices. It sends &lt;Polling Message&gt; to all remote
42402bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * devices.
42502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     *
42602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param callback an interface used to get a list of all remote devices' address
4270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @param pickStrategy strategy how to pick polling candidates
42802bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     * @param retryCount the number of retry used to send polling message to remote devices
4290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
43002bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang     */
4310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
4320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
4330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    }
4340f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang
4350f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    private int checkPollStrategy(int pickStrategy) {
4360f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        int strategy = pickStrategy & POLL_STRATEGY_MASK;
4370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (strategy == 0) {
4380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
4390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        int iterationStrategy = pickStrategy & POLL_ITERATION_STRATEGY_MASK;
4410f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        if (iterationStrategy == 0) {
4420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang            throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
4430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        }
4440f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang        return strategy | iterationStrategy;
44502bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang    }
44602bb4265ac41e1974ec7d4793e6c2a0ed2adc3c4Jungshik Jang
4473ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    /**
4483ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Launch device discovery sequence. It starts with clearing the existing device info list.
4493ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * Note that it assumes that logical address of all local devices is already allocated.
4503ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     *
4513ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     * @param sourceAddress a logical address of tv
4523ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang     */
4530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang    void launchDeviceDiscovery(final int sourceAddress) {
4543ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // At first, clear all existing device infos.
4553ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        mCecController.clearDeviceInfoList();
456092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        // TODO: flush cec message cache when CEC is turned off.
4573ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4583ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
4593ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                new DeviceDiscoveryCallback() {
4603ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    @Override
4613ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
4623ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        for (HdmiCecDeviceInfo info : deviceInfos) {
463a466929979a92a578d4ba00093fefa57cfb982b4Jungshik Jang                            addCecDevice(info);
4643ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        }
4653ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4663ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        // Add device info of all local devices.
4673ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
468a466929979a92a578d4ba00093fefa57cfb982b4Jungshik Jang                            addCecDevice(device.getDeviceInfo());
4693ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                        }
4703ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                        addAndStartAction(new HotplugDetectionAction(HdmiControlService.this,
4720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang                                sourceAddress));
4733ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                    }
4743ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                });
4753ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        addAndStartAction(action);
4763ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4773ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
4783ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
4793ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        // TODO: get device name read from system configuration.
4803ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
4813ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang        return new HdmiCecDeviceInfo(logicalAddress,
4823ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang                getPhysicalAddress(), deviceType, getVendorId(), displayName);
4833ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang    }
4843ee65720e91c7f92ad5a034d7052122a606aa8d5Jungshik Jang
48567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleInitiateArc(HdmiCecMessage message){
48667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Initiate Arc> is started by <Request ARC Initiation>
48767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
48867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcInitiationAction.class);
48967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
49067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), true);
49167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
49267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
49367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
49467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    private void handleTerminateArc(HdmiCecMessage message) {
49567ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // In case where <Terminate Arc> is started by <Request ARC Termination>
49667ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // need to clean up RequestArcInitiationAction.
49767ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // TODO: check conditions of power status by calling is_connected api
49867ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        // to be added soon.
49967ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        removeAction(RequestArcTerminationAction.class);
50067ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        SetArcTransmissionStateAction action = new SetArcTransmissionStateAction(this,
50167ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang                message.getDestination(), message.getSource(), false);
50267ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang        addAndStartAction(action);
50367ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang    }
50467ea521d14f366fe5aac09e512865d31bfa0ee53Jungshik Jang
505c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim    private boolean dispatchMessageToAction(HdmiCecMessage message) {
506c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        for (FeatureAction action : mActions) {
507c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim            if (action.processCommand(message)) {
508c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim                return true;
509c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim            }
510c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        }
511c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        Slog.w(TAG, "Unsupported cec command:" + message);
512c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim        return false;
513c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim    }
514c3923ea50dd0b161ba84941c071d34bb98c0700aJinsuk Kim
51563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void handleSetSystemAudioMode(HdmiCecMessage message) {
51663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) {
51763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return;
51863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
51963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
52063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                message.getDestination(), message.getSource(),
52163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                HdmiUtils.parseCommandParamSystemAudioStatus(message));
52263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        addAndStartAction(action);
52363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
52463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
52563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private void handleSystemAudioModeStatus(HdmiCecMessage message) {
52663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (!isMessageForSystemAudio(message)) {
52763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return;
52863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
52963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
53063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
53163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
53263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private boolean isMessageForSystemAudio(HdmiCecMessage message) {
53363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM
53463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                || message.getDestination() != HdmiCec.ADDR_TV
53563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                || getAvrDeviceInfo() == null) {
53663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            Slog.w(TAG, "Skip abnormal CecMessage: " + message);
53763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return false;
53863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
53963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        return true;
54063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
54163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
54278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // Record class that monitors the event of the caller of being killed. Used to clean up
54378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    // the listener list and record list accordingly.
54478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class HotplugEventListenerRecord implements IBinder.DeathRecipient {
54578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        private final IHdmiHotplugEventListener mListener;
54678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
54778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public HotplugEventListenerRecord(IHdmiHotplugEventListener listener) {
54878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mListener = listener;
54978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
55078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
55178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
55278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public void binderDied() {
55378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
55478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListenerRecords.remove(this);
55578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                mHotplugEventListeners.remove(mListener);
55678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
55778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
55878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
55978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
560cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void addCecDevice(HdmiCecDeviceInfo info) {
561cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCecController.addDeviceInfo(info);
562cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
563cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
56478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void enforceAccessPermission() {
56578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
56678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
56778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
56878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private final class BinderService extends IHdmiControlService.Stub {
56978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
57078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        public int[] getSupportedTypes() {
57178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
57278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            synchronized (mLock) {
57378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                return mLocalDevices;
57478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
57578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
57678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
57778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5787fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void oneTouchPlay(final IHdmiControlCallback callback) {
57978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5807fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5817fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5827fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5837fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.oneTouchPlay(callback);
5847fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5857fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
58678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
58778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
58878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
5897fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void queryDisplayStatus(final IHdmiControlCallback callback) {
59078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
5917fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
5927fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
5937fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
5947fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.queryDisplayStatus(callback);
5957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
5967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
59778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
59878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
59978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
60178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6037fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6047fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6057fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.addHotplugEventListener(listener);
6067fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6077fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
60878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
60978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
61078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        @Override
6117fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
61278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            enforceAccessPermission();
6137fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            runOnServiceThread(new Runnable() {
6147fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                @Override
6157fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                public void run() {
6167fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                    HdmiControlService.this.removeHotplugEventListener(listener);
6177fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                }
6187fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            });
61978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
62078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
62178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
62278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void oneTouchPlay(IHdmiControlCallback callback) {
6237fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(OneTouchPlayAction.class)) {
6247fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "oneTouchPlay already in progress");
6257fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
6267fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6277fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6287fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
6297fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
6307fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
6317fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
6327fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6337fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6347fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        // TODO: Consider the case of multiple TV sets. For now we always direct the command
6357fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        //       to the primary one.
6367fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        OneTouchPlayAction action = OneTouchPlayAction.create(this,
6377fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(),
6387fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getPhysicalAddress(), HdmiCec.ADDR_TV, callback);
6397fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
6407fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate oneTouchPlay");
6417fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
6427fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6437fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6447fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
64578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
64678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
64778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void queryDisplayStatus(IHdmiControlCallback callback) {
6487fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (hasAction(DevicePowerStatusAction.class)) {
6497fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "queryDisplayStatus already in progress");
6507fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_ALREADY_IN_PROGRESS);
6517fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6527fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6537fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        HdmiCecLocalDevice source = mCecController.getLocalDevice(HdmiCec.DEVICE_PLAYBACK);
6547fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (source == null) {
6557fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Local playback device not available");
6567fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
6577fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6587fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6597fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        DevicePowerStatusAction action = DevicePowerStatusAction.create(this,
6607fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim                source.getDeviceInfo().getLogicalAddress(), HdmiCec.ADDR_TV, callback);
6617fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        if (action == null) {
6627fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.w(TAG, "Cannot initiate queryDisplayStatus");
6637fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            invokeCallback(callback, HdmiCec.RESULT_EXCEPTION);
6647fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            return;
6657fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
6667fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        addAndStartAction(action);
66778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
66878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
66978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void addHotplugEventListener(IHdmiHotplugEventListener listener) {
67078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
67178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        try {
67278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            listener.asBinder().linkToDeath(record, 0);
67378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        } catch (RemoteException e) {
67478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            Slog.w(TAG, "Listener already died");
67578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            return;
67678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
67778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
67878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListenerRecords.add(record);
67978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.add(listener);
68078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
68178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
68278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim
68378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    private void removeHotplugEventListener(IHdmiHotplugEventListener listener) {
68478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        synchronized (mLock) {
68578d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            for (HotplugEventListenerRecord record : mHotplugEventListenerRecords) {
68678d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                if (record.mListener.asBinder() == listener.asBinder()) {
68778d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    listener.asBinder().unlinkToDeath(record, 0);
68878d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    mHotplugEventListenerRecords.remove(record);
68978d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                    break;
69078d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim                }
69178d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            }
69278d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim            mHotplugEventListeners.remove(listener);
69378d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim        }
69478d695d8ba532214b02e7f18e0ccf89cf099163dJinsuk Kim    }
6957fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim
6967fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    private void invokeCallback(IHdmiControlCallback callback, int result) {
6977fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        try {
6987fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            callback.onComplete(result);
6997fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        } catch (RemoteException e) {
7007fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim            Slog.e(TAG, "Invoking callback failed:" + e);
7017fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim        }
7027fe2ae0fe9c24f0a1a5ddf20850069b56af2c2fdJinsuk Kim    }
70363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
70463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    HdmiCecDeviceInfo getAvrDeviceInfo() {
70563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
70663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
70763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
70863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    void setAudioStatus(boolean mute, int volume) {
70963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Hook up with AudioManager.
71063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
71163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
71263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    boolean isInPresetInstallationMode() {
71363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        // TODO: Implement this.
71463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        return false;
71563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
716e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
717e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    /**
718e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang     * Called when a device is removed or removal of device is detected.
719e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang     *
720e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang     * @param address a logical address of a device to be removed
721e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang     */
722e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    void removeCecDevice(int address) {
723e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        mCecController.removeDeviceInfo(address);
724e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        mCecMessageCache.flushMessagesFrom(address);
725e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
726e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
727e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    HdmiCecMessageCache getCecMessageCache() {
728e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        return mCecMessageCache;
729e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
7300792d37385e60aa8d73f8df174d0a32f4f618bc4Jungshik Jang}
731