HdmiCecLocalDeviceTv.java revision 0a3316bcfdac9f5f40d1349d97d10329c70f7e30
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.IHdmiControlCallback;
20import android.hardware.hdmi.HdmiCec;
21import android.hardware.hdmi.HdmiCecDeviceInfo;
22import android.hardware.hdmi.HdmiCecMessage;
23import android.os.RemoteException;
24import android.util.Slog;
25
26import java.util.Collections;
27import java.util.List;
28import java.util.Locale;
29
30/**
31 * Represent a logical device of type TV residing in Android system.
32 */
33final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
34    private static final String TAG = "HdmiCecLocalDeviceTv";
35
36    HdmiCecLocalDeviceTv(HdmiControlService service) {
37        super(service, HdmiCec.DEVICE_TV);
38    }
39
40    @Override
41    protected void onAddressAllocated(int logicalAddress) {
42        // TODO: vendor-specific initialization here.
43
44        mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
45                mAddress, mService.getPhysicalAddress(), mDeviceType));
46        mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
47                mAddress, mService.getVendorId()));
48
49        mService.launchDeviceDiscovery(mAddress);
50        // TODO: Start routing control action, device discovery action.
51    }
52
53    @Override
54    protected boolean onMessage(HdmiCecMessage message) {
55        switch (message.getOpcode()) {
56            case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
57                return handleReportPhysicalAddress(message);
58            default:
59                return super.onMessage(message);
60        }
61    }
62
63    /**
64     * Performs the action 'device select', or 'one touch play' initiated by TV.
65     *
66     * @param targetAddress logical address of the device to select
67     * @param callback callback object to report the result with
68     */
69    void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
70        HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
71        if (targetDevice == null) {
72            invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
73            return;
74        }
75        mService.removeAction(DeviceSelectAction.class);
76        mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
77                mService.getPhysicalAddress(), targetDevice, callback));
78    }
79
80    private static void invokeCallback(IHdmiControlCallback callback, int result) {
81        try {
82            callback.onComplete(result);
83        } catch (RemoteException e) {
84            Slog.e(TAG, "Invoking callback failed:" + e);
85        }
86    }
87
88    @Override
89    protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
90        HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
91                mAddress, Locale.getDefault().getISO3Language());
92        // TODO: figure out how to handle failed to get language code.
93        if (command != null) {
94            mService.sendCecCommand(command);
95        } else {
96            Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
97        }
98        return true;
99    }
100
101    private boolean handleReportPhysicalAddress(HdmiCecMessage message) {
102        // Ignore if [Device Discovery Action] is going on.
103        if (mService.hasAction(DeviceDiscoveryAction.class)) {
104            Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
105                    + "because Device Discovery Action is on-going:" + message);
106            return true;
107        }
108
109        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
110        mService.addAndStartAction(new NewDeviceAction(mService,
111                mAddress, message.getSource(), physicalAddress));
112
113        return true;
114    }
115
116    @Override
117    protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
118        List<VendorSpecificAction> actions = Collections.emptyList();
119        // TODO: Call mService.getActions(VendorSpecificAction.class) to get all the actions.
120
121        // We assume that there can be multiple vendor-specific command actions running
122        // at the same time. Pass the message to each action to see if one of them needs it.
123        for (VendorSpecificAction action : actions) {
124            if (action.processCommand(message)) {
125                return true;
126            }
127        }
128        // Handle the message here if it is not already consumed by one of the running actions.
129        // Respond with a appropriate vendor-specific command or <Feature Abort>, or create another
130        // vendor-specific action:
131        //
132        // mService.addAndStartAction(new VendorSpecificAction(mService, mAddress));
133        //
134        // For now, simply reply with <Feature Abort> and mark it consumed by returning true.
135        mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(
136                message.getDestination(), message.getSource(), message.getOpcode(),
137                HdmiConstants.ABORT_REFUSED));
138        return true;
139    }
140}
141