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