HdmiCecLocalDeviceTv.java revision 60cffce420db4c3395f86d3b9bb36003adf26f5d
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.hdmi;
18
19import android.hardware.hdmi.HdmiCec;
20import android.hardware.hdmi.HdmiCecDeviceInfo;
21import android.hardware.hdmi.HdmiCecMessage;
22import android.hardware.hdmi.IHdmiControlCallback;
23import android.os.RemoteException;
24import android.util.Slog;
25
26import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
27
28import java.util.Collections;
29import java.util.List;
30import java.util.Locale;
31
32/**
33 * Represent a logical device of type TV residing in Android system.
34 */
35final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
36    private static final String TAG = "HdmiCecLocalDeviceTv";
37
38    HdmiCecLocalDeviceTv(HdmiControlService service) {
39        super(service, HdmiCec.DEVICE_TV);
40    }
41
42    @Override
43    protected void onAddressAllocated(int logicalAddress) {
44        // TODO: vendor-specific initialization here.
45
46        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
47                mAddress, mService.getPhysicalAddress(), mDeviceType));
48        mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
49                mAddress, mService.getVendorId()));
50
51        launchDeviceDiscovery();
52        // TODO: Start routing control action, device discovery action.
53    }
54
55    /**
56     * Performs the action 'device select', or 'one touch play' initiated by TV.
57     *
58     * @param targetAddress logical address of the device to select
59     * @param callback callback object to report the result with
60     */
61    void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
62        HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
63        if (targetDevice == null) {
64            invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
65            return;
66        }
67        mService.removeAction(DeviceSelectAction.class);
68        mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
69                mService.getPhysicalAddress(), targetDevice, callback));
70    }
71
72    private static void invokeCallback(IHdmiControlCallback callback, int result) {
73        try {
74            callback.onComplete(result);
75        } catch (RemoteException e) {
76            Slog.e(TAG, "Invoking callback failed:" + e);
77        }
78    }
79
80    @Override
81    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
82        HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
83                mAddress, Locale.getDefault().getISO3Language());
84        // TODO: figure out how to handle failed to get language code.
85        if (command != null) {
86            mService.sendCecCommand(command);
87        } else {
88            Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
89        }
90        return true;
91    }
92
93    @Override
94    protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
95        // Ignore if [Device Discovery Action] is going on.
96        if (mService.hasAction(DeviceDiscoveryAction.class)) {
97            Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
98                    + "because Device Discovery Action is on-going:" + message);
99            return true;
100        }
101
102        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
103        int logicalAddress = message.getSource();
104
105        // If it is a new device and connected to the tail of active path,
106        // it's required to change routing path.
107        boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress)
108                && mService.isTailOfActivePath(physicalAddress);
109        mService.addAndStartAction(new NewDeviceAction(mService,
110                mAddress, message.getSource(), physicalAddress,
111                requireRoutingChange));
112        return true;
113    }
114
115    @Override
116    protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
117        List<VendorSpecificAction> actions = Collections.emptyList();
118        // TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
119
120        // We assume that there can be multiple vendor-specific command actions running
121        // at the same time. Pass the message to each action to see if one of them needs it.
122        for (VendorSpecificAction action : actions) {
123            if (action.processCommand(message)) {
124                return true;
125            }
126        }
127        // Handle the message here if it is not already consumed by one of the running actions.
128        // Respond with a appropriate vendor-specific command or <Feature Abort>, or create another
129        // vendor-specific action:
130        //
131        // mService.addAndStartAction(new VendorSpecificAction(mService, mAddress));
132        //
133        // For now, simply reply with <Feature Abort> and mark it consumed by returning true.
134        mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
135                message.getDestination(), message.getSource(), message.getOpcode(),
136                HdmiConstants.ABORT_REFUSED));
137        return true;
138    }
139
140    private void launchDeviceDiscovery() {
141        mService.clearAllDeviceInfo();
142        // TODO: Move the following callback to HdmiLocalDeviceTv.
143        DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress,
144                new DeviceDiscoveryCallback() {
145                    @Override
146                    public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
147                        for (HdmiCecDeviceInfo info : deviceInfos) {
148                            mService.addCecDevice(info);
149                        }
150
151                        // Since we removed all devices when it's start and
152                        // device discovery action does not poll local devices,
153                        // we should put device info of local device manually here
154                        for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
155                            mService.addCecDevice(device.getDeviceInfo());
156                        }
157
158                        mService.addAndStartAction(new HotplugDetectionAction(mService,
159                                mAddress));
160                    }
161                });
162        mService.addAndStartAction(action);
163    }
164}
165