1cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang/*
2cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Copyright (C) 2014 The Android Open Source Project
3cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *
4cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License");
5cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * you may not use this file except in compliance with the License.
6cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * You may obtain a copy of the License at
7cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *
8cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *      http://www.apache.org/licenses/LICENSE-2.0
9cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *
10cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Unless required by applicable law or agreed to in writing, software
11cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS,
12cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * See the License for the specific language governing permissions and
14cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * limitations under the License.
15cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang */
16cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
17cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangpackage com.android.server.hdmi;
18cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
20cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport android.util.Slog;
21cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
22cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport com.android.internal.util.Preconditions;
23cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
24cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
25cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport java.io.UnsupportedEncodingException;
26cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport java.util.ArrayList;
27cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jangimport java.util.List;
28cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
29cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang/**
30cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Feature action that handles device discovery sequences.
31cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * Device discovery is launched when TV device is woken from "Standby" state
32cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * or enabled "Control for Hdmi" from disabled state.
33cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *
34cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * <p>Device discovery goes through the following steps.
35cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * <ol>
36cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *   <li>Poll all non-local devices by sending &lt;Polling Message&gt;
37cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *   <li>Gather "Physical address" and "device type" of all acknowledged devices
38cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *   <li>Gather "OSD (display) name" of all acknowledge devices
39cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang *   <li>Gather "Vendor id" of all acknowledge devices
40cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang * </ol>
4146350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo * We attempt to get OSD name/vendor ID up to 5 times in case the communication fails.
42cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang */
43b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangfinal class DeviceDiscoveryAction extends HdmiCecFeatureAction {
44cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final String TAG = "DeviceDiscoveryAction";
45cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
46cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // State in which the action is waiting for device polling.
47cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final int STATE_WAITING_FOR_DEVICE_POLLING = 1;
48cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // State in which the action is waiting for gathering physical address of non-local devices.
49cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final int STATE_WAITING_FOR_PHYSICAL_ADDRESS = 2;
50cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // State in which the action is waiting for gathering osd name of non-local devices.
51cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final int STATE_WAITING_FOR_OSD_NAME = 3;
52cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // State in which the action is waiting for gathering vendor id of non-local devices.
53cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final int STATE_WAITING_FOR_VENDOR_ID = 4;
54cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
55cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    /**
56cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     * Interface used to report result of device discovery.
57cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     */
58cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    interface DeviceDiscoveryCallback {
59cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        /**
60cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang         * Called when device discovery is done.
61cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang         *
62cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang         * @param deviceInfos a list of all non-local devices. It can be empty list.
63cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang         */
6461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos);
65cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
66cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
67cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // An internal container used to keep track of device information during
68cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    // this action.
69cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private static final class DeviceInfo {
70cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        private final int mLogicalAddress;
71cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
72c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
732b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        private int mPortId = Constants.INVALID_PORT_ID;
74c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        private int mVendorId = Constants.UNKNOWN_VENDOR_ID;
75cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        private String mDisplayName = "";
7661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        private int mDeviceType = HdmiDeviceInfo.DEVICE_INACTIVE;
77cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
78cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        private DeviceInfo(int logicalAddress) {
79cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            mLogicalAddress = logicalAddress;
80cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
81cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
8261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        private HdmiDeviceInfo toHdmiDeviceInfo() {
8361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            return new HdmiDeviceInfo(mLogicalAddress, mPhysicalAddress, mPortId, mDeviceType,
842b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim                    mVendorId, mDisplayName);
85cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
86cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
87cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
88cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private final ArrayList<DeviceInfo> mDevices = new ArrayList<>();
89cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private final DeviceDiscoveryCallback mCallback;
90cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private int mProcessedDeviceCount = 0;
9146350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo    private int mTimeoutRetry = 0;
92cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
93cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    /**
94e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang     * Constructor.
95cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     *
9679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang     * @param source an instance of {@link HdmiCecLocalDevice}.
97cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang     */
9879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback) {
9979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        super(source);
100cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCallback = Preconditions.checkNotNull(callback);
101cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
102cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
103cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    @Override
104cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    boolean start() {
105cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mDevices.clear();
106cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mState = STATE_WAITING_FOR_DEVICE_POLLING;
107cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
10879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        pollDevices(new DevicePollingCallback() {
109cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            @Override
110cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            public void onPollingFinished(List<Integer> ackedAddress) {
111cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                if (ackedAddress.isEmpty()) {
1128b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang                    Slog.v(TAG, "No device is detected.");
113ad2eefe97935db5282da18459b8da22b48207d83Jungshik Jang                    wrapUpAndFinish();
114cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return;
115cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                }
116cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
1178b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang                Slog.v(TAG, "Device detected: " + ackedAddress);
118cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                allocateDevices(ackedAddress);
119cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                startPhysicalAddressStage();
120cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            }
121c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        }, Constants.POLL_ITERATION_REVERSE_ORDER
1225fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim            | Constants.POLL_STRATEGY_REMOTES_DEVICES, HdmiConfig.DEVICE_POLLING_RETRY);
123cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        return true;
124cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
125cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
126cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void allocateDevices(List<Integer> addresses) {
127cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        for (Integer i : addresses) {
128cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            DeviceInfo info = new DeviceInfo(i);
129cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            mDevices.add(info);
130cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
131cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
132cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
133cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void startPhysicalAddressStage() {
1348b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "Start [Physical Address Stage]:" + mDevices.size());
135cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mProcessedDeviceCount = 0;
136cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mState = STATE_WAITING_FOR_PHYSICAL_ADDRESS;
137cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
138cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
139cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
140cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
141cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private boolean verifyValidLogicalAddress(int address) {
142c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return address >= Constants.ADDR_TV && address < Constants.ADDR_UNREGISTERED;
143cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
144cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
145cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void queryPhysicalAddress(int address) {
146cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (!verifyValidLogicalAddress(address)) {
147cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            checkAndProceedStage();
148cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
149cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
150cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
151cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mActionTimer.clearTimerMessage();
152e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
153e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        // Check cache first and send request if not exist.
154c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mayProcessMessageIfCached(address, Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
155e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang            return;
156e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        }
15779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(getSourceAddress(), address));
1585fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim        addTimer(mState, HdmiConfig.TIMEOUT_MS);
159cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
160cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
161cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void startOsdNameStage() {
1628b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "Start [Osd Name Stage]:" + mDevices.size());
163cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mProcessedDeviceCount = 0;
164cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mState = STATE_WAITING_FOR_OSD_NAME;
165cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
166cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
167cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
168cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
169cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void queryOsdName(int address) {
170cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (!verifyValidLogicalAddress(address)) {
171cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            checkAndProceedStage();
172cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
173cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
174cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
175cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mActionTimer.clearTimerMessage();
176e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
177c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mayProcessMessageIfCached(address, Constants.MESSAGE_SET_OSD_NAME)) {
178e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang            return;
179e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        }
18079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(getSourceAddress(), address));
1815fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim        addTimer(mState, HdmiConfig.TIMEOUT_MS);
182cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
183cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
184cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void startVendorIdStage() {
1858b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "Start [Vendor Id Stage]:" + mDevices.size());
1868b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang
187cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mProcessedDeviceCount = 0;
188cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mState = STATE_WAITING_FOR_VENDOR_ID;
189cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
190cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
191cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
192cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
193cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void queryVendorId(int address) {
194cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (!verifyValidLogicalAddress(address)) {
195cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            checkAndProceedStage();
196cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
197cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
198cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
199cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mActionTimer.clearTimerMessage();
200e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
201c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (mayProcessMessageIfCached(address, Constants.MESSAGE_DEVICE_VENDOR_ID)) {
202e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang            return;
203e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        }
20479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        sendCommand(
20579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang                HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(getSourceAddress(), address));
2065fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim        addTimer(mState, HdmiConfig.TIMEOUT_MS);
207cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
208cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
209e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    private boolean mayProcessMessageIfCached(int address, int opcode) {
21079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        HdmiCecMessage message = getCecMessageCache().getMessage(address, opcode);
211e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        if (message != null) {
212e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang            processCommand(message);
213e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang            return true;
214e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        }
215e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang        return false;
216e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang    }
217e81e108c4035ea8933525baa8108cb392f8abf5dJungshik Jang
218cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    @Override
219cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    boolean processCommand(HdmiCecMessage cmd) {
220cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        switch (mState) {
221cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
222c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (cmd.getOpcode() == Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS) {
223cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    handleReportPhysicalAddress(cmd);
224cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return true;
225cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                }
226cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                return false;
227cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            case STATE_WAITING_FOR_OSD_NAME:
228c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (cmd.getOpcode() == Constants.MESSAGE_SET_OSD_NAME) {
229cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    handleSetOsdName(cmd);
230cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return true;
231cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                }
232cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                return false;
233cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            case STATE_WAITING_FOR_VENDOR_ID:
234c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim                if (cmd.getOpcode() == Constants.MESSAGE_DEVICE_VENDOR_ID) {
235cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    handleVendorId(cmd);
236cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return true;
237cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                }
238cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                return false;
239cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            case STATE_WAITING_FOR_DEVICE_POLLING:
240cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                // Fall through.
241cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            default:
242cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                return false;
243cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
244cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
245cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
246cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void handleReportPhysicalAddress(HdmiCecMessage cmd) {
247cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
248cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
249cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
250cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (current.mLogicalAddress != cmd.getSource()) {
251cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
252cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    cmd.getSource());
253cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
254cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
255cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
256cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        byte params[] = cmd.getParams();
25775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
2582b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        current.mPortId = getPortId(current.mPhysicalAddress);
25975a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        current.mDeviceType = params[2] & 0xFF;
260cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
261bcfa0677d4b015457b73b1147c96e4ad2946b2beJinsuk Kim        tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
262bcfa0677d4b015457b73b1147c96e4ad2946b2beJinsuk Kim                    current.mPhysicalAddress);
263bcfa0677d4b015457b73b1147c96e4ad2946b2beJinsuk Kim
26475a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        increaseProcessedDeviceCount();
26575a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        checkAndProceedStage();
266cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
267cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
2682b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    private int getPortId(int physicalAddress) {
2692b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim        return tv().getPortId(physicalAddress);
2702b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim    }
2712b152015ff94f20b9ec3ef284fb83105f8b3c831Jinsuk Kim
272cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void handleSetOsdName(HdmiCecMessage cmd) {
273cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
274cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
275cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
276cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (current.mLogicalAddress != cmd.getSource()) {
277cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
278cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    cmd.getSource());
279cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
280cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
281cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
282cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        String displayName = null;
283cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        try {
284cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            displayName = new String(cmd.getParams(), "US-ASCII");
285cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        } catch (UnsupportedEncodingException e) {
286cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
287cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            // If failed to get display name, use the default name of device.
288c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
289cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
290cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        current.mDisplayName = displayName;
291cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        increaseProcessedDeviceCount();
292cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
293cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
294cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
295cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void handleVendorId(HdmiCecMessage cmd) {
296cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        Preconditions.checkState(mProcessedDeviceCount < mDevices.size());
297cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
298cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        DeviceInfo current = mDevices.get(mProcessedDeviceCount);
299cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (current.mLogicalAddress != cmd.getSource()) {
300cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            Slog.w(TAG, "Unmatched address[expected:" + current.mLogicalAddress + ", actual:" +
301cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    cmd.getSource());
302cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
303cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
304cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
305cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        byte[] params = cmd.getParams();
30675a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        int vendorId = HdmiUtils.threeBytesToInt(params);
30775a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        current.mVendorId = vendorId;
308cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        increaseProcessedDeviceCount();
309cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
310cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
311cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
312cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void increaseProcessedDeviceCount() {
313cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mProcessedDeviceCount++;
31446350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        mTimeoutRetry = 0;
315cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
316cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
317cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void removeDevice(int index) {
318cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mDevices.remove(index);
319cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
320cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
321cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void wrapUpAndFinish() {
3228b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "---------Wrap up Device Discovery:[" + mDevices.size() + "]---------");
32361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        ArrayList<HdmiDeviceInfo> result = new ArrayList<>();
324cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        for (DeviceInfo info : mDevices) {
32561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang            HdmiDeviceInfo cecDeviceInfo = info.toHdmiDeviceInfo();
3268b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang            Slog.v(TAG, " DeviceInfo: " + cecDeviceInfo);
327cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            result.add(cecDeviceInfo);
328cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
3298b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "--------------------------------------------");
330cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        mCallback.onDeviceDiscoveryDone(result);
331cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        finish();
3327fa3a66470d2133796defd14a0600578758882acJinsuk Kim        // Process any commands buffered while device discovery action was in progress.
3337fa3a66470d2133796defd14a0600578758882acJinsuk Kim        tv().processAllDelayedMessages();
334cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
335cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
336cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    private void checkAndProceedStage() {
337cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (mDevices.isEmpty()) {
338cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            wrapUpAndFinish();
339cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
340cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
341cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
342cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        // If finished current stage, move on to next stage.
343cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (mProcessedDeviceCount == mDevices.size()) {
344cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            mProcessedDeviceCount = 0;
345cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            switch (mState) {
346cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
347cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    startOsdNameStage();
348cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return;
349cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                case STATE_WAITING_FOR_OSD_NAME:
350cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    startVendorIdStage();
351cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return;
352cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                case STATE_WAITING_FOR_VENDOR_ID:
353cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    wrapUpAndFinish();
354cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return;
355cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                default:
356cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang                    return;
357cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            }
358cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        } else {
35946350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            sendQueryCommand();
36046350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        }
36146350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo    }
36246350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo
36346350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo    private void sendQueryCommand() {
36446350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        int address = mDevices.get(mProcessedDeviceCount).mLogicalAddress;
36546350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        switch (mState) {
36646350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            case STATE_WAITING_FOR_PHYSICAL_ADDRESS:
36746350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                queryPhysicalAddress(address);
36846350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                return;
36946350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            case STATE_WAITING_FOR_OSD_NAME:
37046350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                queryOsdName(address);
37146350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                return;
37246350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            case STATE_WAITING_FOR_VENDOR_ID:
37346350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                queryVendorId(address);
37446350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            default:
37546350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo                return;
376cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
377cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
378cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
379cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    @Override
380cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    void handleTimerEvent(int state) {
381cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        if (mState == STATE_NONE || mState != state) {
382cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang            return;
383cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        }
384cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang
38546350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        if (++mTimeoutRetry < HdmiConfig.TIMEOUT_RETRY) {
38646350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            sendQueryCommand();
38746350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo            return;
38846350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        }
38946350382ee1974fd635d43c7273dc44854edf7c7Yuncheol Heo        mTimeoutRetry = 0;
3908b308d93c8fdcc7304b33d9b445ae3807eae97c8Jungshik Jang        Slog.v(TAG, "Timeout[State=" + mState + ", Processed=" + mProcessedDeviceCount);
391cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        removeDevice(mProcessedDeviceCount);
392cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang        checkAndProceedStage();
393cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang    }
394cc5ef8c918e96516a5c51cc40735a1b8a24d8497Jungshik Jang}
395