HdmiControlService.java revision a8a5e50c6f9ba3ae0ff59eda76354e93515d6f8f
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.annotation.Nullable;
20import android.content.Context;
21import android.hardware.hdmi.HdmiCec;
22import android.hardware.hdmi.HdmiCecDeviceInfo;
23import android.hardware.hdmi.HdmiCecMessage;
24import android.os.HandlerThread;
25import android.os.Looper;
26import android.util.Slog;
27
28import com.android.server.SystemService;
29
30import java.util.Locale;
31
32/**
33 * Provides a service for sending and processing HDMI control messages,
34 * HDMI-CEC and MHL control command, and providing the information on both standard.
35 */
36public final class HdmiControlService extends SystemService {
37    private static final String TAG = "HdmiControlService";
38
39    // A thread to handle synchronous IO of CEC and MHL control service.
40    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
41    // and sparse call it shares a thread to handle IO operations.
42    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
43
44    @Nullable
45    private HdmiCecController mCecController;
46
47    @Nullable
48    private HdmiMhlController mMhlController;
49
50    public HdmiControlService(Context context) {
51        super(context);
52    }
53
54    @Override
55    public void onStart() {
56        mCecController = HdmiCecController.create(this);
57        if (mCecController != null) {
58            mCecController.initializeLocalDevices(getContext().getResources()
59                    .getIntArray(com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
60        } else {
61            Slog.i(TAG, "Device does not support HDMI-CEC.");
62        }
63
64        mMhlController = HdmiMhlController.create(this);
65        if (mMhlController == null) {
66            Slog.i(TAG, "Device does not support MHL-control.");
67        }
68    }
69
70    /**
71     * Returns {@link Looper} for IO operation.
72     *
73     * <p>Declared as package-private.
74     */
75    Looper getIoLooper() {
76        return mIoThread.getLooper();
77    }
78
79    /**
80     * Returns {@link Looper} of main thread. Use this {@link Looper} instance
81     * for tasks that are running on main service thread.
82     *
83     * <p>Declared as package-private.
84     */
85    Looper getServiceLooper() {
86        return Looper.myLooper();
87    }
88
89    /**
90     * Add a new {@link FeatureAction} to the action queue.
91     *
92     * @param action {@link FeatureAction} to add
93     */
94    void addAction(FeatureAction action) {
95        // TODO: Implement this.
96    }
97
98
99    /**
100     * Remove the given {@link FeatureAction} object from the action queue.
101     *
102     * @param action {@link FeatureAction} to add
103     */
104    void removeAction(FeatureAction action) {
105        // TODO: Implement this.
106    }
107
108    /**
109     * Transmit a CEC command to CEC bus.
110     *
111     * @param command CEC command to send out
112     */
113    void sendCecCommand(HdmiCecMessage command) {
114        mCecController.sendCommand(command);
115    }
116
117    /**
118     * Add a new {@link HdmiCecDeviceInfo} to controller.
119     *
120     * @param deviceInfo new device information object to add
121     */
122    void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
123        // TODO: Implement this.
124    }
125
126    boolean handleCecCommand(HdmiCecMessage message) {
127        // Commands that queries system information replies directly instead
128        // of creating FeatureAction because they are state-less.
129        switch (message.getOpcode()) {
130            case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
131                handleGetMenuLanguage(message);
132                return true;
133            case HdmiCec.MESSAGE_GET_OSD_NAME:
134                handleGetOsdName(message);
135                return true;
136            case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
137                handleGivePhysicalAddress(message);
138                return true;
139            case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
140                handleGiveDeviceVendorId(message);
141                return true;
142            case HdmiCec.MESSAGE_GET_CEC_VERSION:
143                handleGetCecVersion(message);
144                return true;
145            // TODO: Add remaining system information query such as
146            // <Give Device Power Status> and <Request Active Source> handler.
147            default:
148                Slog.w(TAG, "Unsupported cec command:" + message.toString());
149                return false;
150        }
151    }
152
153    private void handleGetCecVersion(HdmiCecMessage message) {
154        int version = mCecController.getVersion();
155        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
156                message.getSource(),
157                version);
158        sendCecCommand(cecMessage);
159    }
160
161    private void handleGiveDeviceVendorId(HdmiCecMessage message) {
162        int vendorId = mCecController.getVendorId();
163        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
164                message.getDestination(), vendorId);
165        sendCecCommand(cecMessage);
166    }
167
168    private void handleGivePhysicalAddress(HdmiCecMessage message) {
169        int physicalAddress = mCecController.getPhysicalAddress();
170        int deviceType = HdmiCec.getTypeFromAddress(message.getDestination());
171        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
172                message.getDestination(), physicalAddress, deviceType);
173        sendCecCommand(cecMessage);
174    }
175
176    private void handleGetOsdName(HdmiCecMessage message) {
177        // TODO: read device name from settings or property.
178        String name = HdmiCec.getDefaultDeviceName(message.getDestination());
179        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
180                message.getDestination(), message.getSource(), name);
181        if (cecMessage != null) {
182            sendCecCommand(cecMessage);
183        } else {
184            Slog.w(TAG, "Failed to build <Get Osd Name>:" + name);
185        }
186    }
187
188    private void handleGetMenuLanguage(HdmiCecMessage message) {
189        // Only 0 (TV), 14 (specific use) can answer.
190        if (message.getDestination() != HdmiCec.ADDR_TV
191                && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) {
192            Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
193            sendCecCommand(
194                    HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
195                            message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
196                            HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
197            return;
198        }
199
200        HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
201                message.getDestination(),
202                Locale.getDefault().getISO3Language());
203        // TODO: figure out how to handle failed to get language code.
204        if (command != null) {
205            sendCecCommand(command);
206        } else {
207            Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
208        }
209    }
210}
211