HdmiControlService.java revision be9cd8eb3fe64a572f9c7dfc41f04defd46a752d
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 Slog.i(TAG, "Device does not support HDMI-CEC."); 59 } 60 61 mMhlController = HdmiMhlController.create(this); 62 if (mMhlController == null) { 63 Slog.i(TAG, "Device does not support MHL-control."); 64 } 65 } 66 67 /** 68 * Returns {@link Looper} for IO operation. 69 * 70 * <p>Declared as package-private. 71 */ 72 Looper getIoLooper() { 73 return mIoThread.getLooper(); 74 } 75 76 /** 77 * Returns {@link Looper} of main thread. Use this {@link Looper} instance 78 * for tasks that are running on main service thread. 79 * 80 * <p>Declared as package-private. 81 */ 82 Looper getServiceLooper() { 83 return Looper.myLooper(); 84 } 85 86 /** 87 * Add a new {@link FeatureAction} to the action queue. 88 * 89 * @param action {@link FeatureAction} to add 90 */ 91 void addAction(FeatureAction action) { 92 // TODO: Implement this. 93 } 94 95 96 /** 97 * Remove the given {@link FeatureAction} object from the action queue. 98 * 99 * @param action {@link FeatureAction} to add 100 */ 101 void removeAction(FeatureAction action) { 102 // TODO: Implement this. 103 } 104 105 /** 106 * Transmit a CEC command to CEC bus. 107 * 108 * @param command CEC command to send out 109 */ 110 void sendCecCommand(HdmiCecMessage command) { 111 mCecController.sendCommand(command); 112 } 113 114 /** 115 * Add a new {@link HdmiCecDeviceInfo} to controller. 116 * 117 * @param deviceInfo new device information object to add 118 */ 119 void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { 120 // TODO: Implement this. 121 } 122 123 boolean handleCecCommand(HdmiCecMessage message) { 124 // Commands that queries system information replies directly instead 125 // of creating FeatureAction because they are state-less. 126 switch (message.getOpcode()) { 127 case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: 128 handleGetMenuLanguage(message); 129 return true; 130 case HdmiCec.MESSAGE_GIVE_OSD_NAME: 131 handleGiveOsdName(message); 132 return true; 133 case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: 134 handleGivePhysicalAddress(message); 135 return true; 136 case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID: 137 handleGiveDeviceVendorId(message); 138 return true; 139 case HdmiCec.MESSAGE_GET_CEC_VERSION: 140 handleGetCecVersion(message); 141 return true; 142 // TODO: Add remaining system information query such as 143 // <Give Device Power Status> and <Request Active Source> handler. 144 default: 145 Slog.w(TAG, "Unsupported cec command:" + message.toString()); 146 return false; 147 } 148 } 149 150 private void handleGetCecVersion(HdmiCecMessage message) { 151 int version = mCecController.getVersion(); 152 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), 153 message.getSource(), 154 version); 155 sendCecCommand(cecMessage); 156 } 157 158 private void handleGiveDeviceVendorId(HdmiCecMessage message) { 159 int vendorId = mCecController.getVendorId(); 160 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( 161 message.getDestination(), vendorId); 162 sendCecCommand(cecMessage); 163 } 164 165 private void handleGivePhysicalAddress(HdmiCecMessage message) { 166 int physicalAddress = mCecController.getPhysicalAddress(); 167 int deviceType = HdmiCec.getTypeFromAddress(message.getDestination()); 168 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( 169 message.getDestination(), physicalAddress, deviceType); 170 sendCecCommand(cecMessage); 171 } 172 173 private void handleGiveOsdName(HdmiCecMessage message) { 174 // TODO: read device name from settings or property. 175 String name = HdmiCec.getDefaultDeviceName(message.getDestination()); 176 HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand( 177 message.getDestination(), message.getSource(), name); 178 if (cecMessage != null) { 179 sendCecCommand(cecMessage); 180 } else { 181 Slog.w(TAG, "Failed to build <Get Osd Name>:" + name); 182 } 183 } 184 185 private void handleGetMenuLanguage(HdmiCecMessage message) { 186 // Only 0 (TV), 14 (specific use) can answer. 187 if (message.getDestination() != HdmiCec.ADDR_TV 188 && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) { 189 Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString()); 190 sendCecCommand( 191 HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(), 192 message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, 193 HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE)); 194 return; 195 } 196 197 HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( 198 message.getDestination(), 199 Locale.getDefault().getISO3Language()); 200 // TODO: figure out how to handle failed to get language code. 201 if (command != null) { 202 sendCecCommand(command); 203 } else { 204 Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString()); 205 } 206 } 207} 208