Merge "Implement handlers for system information query command."
This commit is contained in:
committed by
Android (Google) Code Review
commit
3bb41a20db
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import android.hardware.hdmi.HdmiCec;
|
||||
import android.hardware.hdmi.HdmiCecMessage;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -155,23 +154,10 @@ abstract class FeatureAction {
|
||||
mActionTimer.sendTimerMessage(state, delayMillis);
|
||||
}
|
||||
|
||||
static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) {
|
||||
return new HdmiCecMessage(src, dst, opcode, params);
|
||||
}
|
||||
|
||||
// Build a CEC command that does not have parameter.
|
||||
static HdmiCecMessage buildCommand(int src, int dst, int opcode) {
|
||||
return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM);
|
||||
}
|
||||
|
||||
protected final void sendCommand(HdmiCecMessage cmd) {
|
||||
mService.sendCecCommand(cmd);
|
||||
}
|
||||
|
||||
protected final void sendBroadcastCommand(int opcode, byte[] param) {
|
||||
sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish up the action. Reset the state, and remove itself from the action queue.
|
||||
*/
|
||||
@@ -189,23 +175,4 @@ abstract class FeatureAction {
|
||||
private void removeAction(FeatureAction action) {
|
||||
mService.removeAction(action);
|
||||
}
|
||||
|
||||
// Utility methods for generating parameter byte arrays for CEC commands.
|
||||
protected static byte[] uiCommandParam(int uiCommand) {
|
||||
return new byte[] {(byte) uiCommand};
|
||||
}
|
||||
|
||||
protected static byte[] physicalAddressParam(int physicalAddress) {
|
||||
return new byte[] {
|
||||
(byte) ((physicalAddress >> 8) & 0xFF),
|
||||
(byte) (physicalAddress & 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
protected static byte[] pathPairParam(int oldPath, int newPath) {
|
||||
return new byte[] {
|
||||
(byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
|
||||
(byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,15 +55,6 @@ final class HdmiCecController {
|
||||
// A message to report allocated logical address to main control looper.
|
||||
private final static int MSG_REPORT_LOGICAL_ADDRESS = 2;
|
||||
|
||||
// TODO: move these values to HdmiCec.java once make it internal constant class.
|
||||
// CEC's ABORT reason values.
|
||||
private static final int ABORT_UNRECOGNIZED_MODE = 0;
|
||||
private static final int ABORT_NOT_IN_CORRECT_MODE = 1;
|
||||
private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
|
||||
private static final int ABORT_INVALID_OPERAND = 3;
|
||||
private static final int ABORT_REFUSED = 4;
|
||||
private static final int ABORT_UNABLE_TO_DETERMINE = 5;
|
||||
|
||||
private static final int NUM_LOGICAL_ADDRESS = 16;
|
||||
|
||||
// TODO: define other constants for errors.
|
||||
@@ -80,10 +71,16 @@ final class HdmiCecController {
|
||||
// interacts with HAL.
|
||||
private long mNativePtr;
|
||||
|
||||
private HdmiControlService mService;
|
||||
|
||||
// Map-like container of all cec devices. A logical address of device is
|
||||
// used as key of container.
|
||||
private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos =
|
||||
new SparseArray<HdmiCecDeviceInfo>();
|
||||
// Set-like container for all local devices' logical address.
|
||||
// Key and value are same.
|
||||
private final SparseArray<Integer> mLocalLogicalAddresses =
|
||||
new SparseArray<Integer>();
|
||||
|
||||
// Private constructor. Use HdmiCecController.create().
|
||||
private HdmiCecController() {
|
||||
@@ -325,6 +322,7 @@ final class HdmiCecController {
|
||||
*/
|
||||
int addLogicalAddress(int newLogicalAddress) {
|
||||
if (HdmiCec.isValidAddress(newLogicalAddress)) {
|
||||
mLocalLogicalAddresses.append(newLogicalAddress, newLogicalAddress);
|
||||
return nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
|
||||
} else {
|
||||
return -1;
|
||||
@@ -337,6 +335,9 @@ final class HdmiCecController {
|
||||
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
|
||||
*/
|
||||
void clearLogicalAddress() {
|
||||
// TODO: consider to backup logical address so that new logical address
|
||||
// allocation can use it as preferred address.
|
||||
mLocalLogicalAddresses.clear();
|
||||
nativeClearLogicalAddress(mNativePtr);
|
||||
}
|
||||
|
||||
@@ -371,30 +372,37 @@ final class HdmiCecController {
|
||||
}
|
||||
|
||||
private void init(HdmiControlService service, long nativePtr) {
|
||||
mService = service;
|
||||
mIoHandler = new IoHandler(service.getServiceLooper());
|
||||
mControlHandler = new ControlHandler(service.getServiceLooper());
|
||||
mNativePtr = nativePtr;
|
||||
}
|
||||
|
||||
private boolean isAcceptableAddress(int address) {
|
||||
// Can access command targeting devices available in local device or
|
||||
// broadcast command.
|
||||
return address == HdmiCec.ADDR_BROADCAST
|
||||
|| mLocalLogicalAddresses.get(address) != null;
|
||||
}
|
||||
|
||||
private void onReceiveCommand(HdmiCecMessage message) {
|
||||
// TODO: Handle message according to opcode type.
|
||||
if (isAcceptableAddress(message.getDestination()) &&
|
||||
mService.handleCecCommand(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Use device's source address for broadcast message.
|
||||
int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ?
|
||||
message.getDestination() : 0;
|
||||
// Reply <Feature Abort> to initiator (source) for all requests.
|
||||
sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(),
|
||||
ABORT_REFUSED);
|
||||
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
|
||||
(sourceAddress, message.getSource(), message.getOpcode(),
|
||||
HdmiCecMessageBuilder.ABORT_REFUSED);
|
||||
sendCommand(cecMessage);
|
||||
|
||||
}
|
||||
|
||||
private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode,
|
||||
int reason) {
|
||||
byte[] params = new byte[2];
|
||||
params[0] = (byte) originalOpcode;
|
||||
params[1] = (byte) reason;
|
||||
|
||||
HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress,
|
||||
HdmiCec.MESSAGE_FEATURE_ABORT, params);
|
||||
void sendCommand(HdmiCecMessage cecMessage) {
|
||||
Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
|
||||
mIoHandler.sendMessage(message);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.hdmi;
|
||||
|
||||
import android.hardware.hdmi.HdmiCec;
|
||||
import android.hardware.hdmi.HdmiCecMessage;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* A helper class to build {@link HdmiCecMessage} from various cec commands.
|
||||
*/
|
||||
public class HdmiCecMessageBuilder {
|
||||
// TODO: move these values to HdmiCec.java once make it internal constant class.
|
||||
// CEC's ABORT reason values.
|
||||
static final int ABORT_UNRECOGNIZED_MODE = 0;
|
||||
static final int ABORT_NOT_IN_CORRECT_MODE = 1;
|
||||
static final int ABORT_CANNOT_PROVIDE_SOURCE = 2;
|
||||
static final int ABORT_INVALID_OPERAND = 3;
|
||||
static final int ABORT_REFUSED = 4;
|
||||
static final int ABORT_UNABLE_TO_DETERMINE = 5;
|
||||
|
||||
private static final int OSD_NAME_MAX_LENGTH = 13;
|
||||
|
||||
private HdmiCecMessageBuilder() {}
|
||||
|
||||
/**
|
||||
* Build <Feature Abort> command. <Feature Abort> consists of
|
||||
* 1 byte original opcode and 1 byte reason fields with basic fields.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @param originalOpcode original opcode causing feature abort
|
||||
* @param reason reason of feature abort
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode,
|
||||
int reason) {
|
||||
byte[] params = new byte[] {
|
||||
(byte) originalOpcode,
|
||||
(byte) reason,
|
||||
};
|
||||
return buildCommand(src, dest, HdmiCec.MESSAGE_FEATURE_ABORT, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Get Osd Name> command.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildGetOsdNameCommand(int src, int dest) {
|
||||
return buildCommand(src, dest, HdmiCec.MESSAGE_GET_OSD_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Give Vendor Id Command> command.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) {
|
||||
return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Set Menu Language > command.
|
||||
*
|
||||
* <p>This is a broadcast message sent to all devices on the bus.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param language 3-letter ISO639-2 based language code
|
||||
* @return newly created {@link HdmiCecMessage} if language is valid.
|
||||
* Otherwise, return null
|
||||
*/
|
||||
static HdmiCecMessage buildSetMenuLanguageCommand(int src, String language) {
|
||||
if (language.length() != 3) {
|
||||
return null;
|
||||
}
|
||||
// Hdmi CEC uses lower-cased ISO 639-2 (3 letters code).
|
||||
String normalized = language.toLowerCase();
|
||||
byte[] params = new byte[] {
|
||||
(byte) normalized.charAt(0),
|
||||
(byte) normalized.charAt(1),
|
||||
(byte) normalized.charAt(2),
|
||||
};
|
||||
// <Set Menu Language> is broadcast message.
|
||||
return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_MENU_LANGUAGE,
|
||||
params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Set Osd Name > command.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param name display (OSD) name of device
|
||||
* @return newly created {@link HdmiCecMessage} if valid name. Otherwise,
|
||||
* return null
|
||||
*/
|
||||
static HdmiCecMessage buildSetOsdNameCommand(int src, int dest, String name) {
|
||||
int length = Math.min(name.length(), OSD_NAME_MAX_LENGTH);
|
||||
byte[] params;
|
||||
try {
|
||||
params = name.substring(0, length).getBytes("US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return null;
|
||||
}
|
||||
return buildCommand(src, dest, HdmiCec.MESSAGE_SET_OSD_NAME, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Report Physical Address> command. It has two bytes physical
|
||||
* address and one byte device type as parameter.
|
||||
*
|
||||
* <p>This is a broadcast message sent to all devices on the bus.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param address physical address of device
|
||||
* @param deviceType type of device
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildReportPhysicalAddressCommand(int src, int address, int deviceType) {
|
||||
byte[] params = new byte[] {
|
||||
// Two bytes for physical address
|
||||
(byte) ((address >> 8) & 0xFF),
|
||||
(byte) (address & 0xFF),
|
||||
// One byte device type
|
||||
(byte) deviceType
|
||||
};
|
||||
// <Report Physical Address> is broadcast message.
|
||||
return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS,
|
||||
params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Device Vendor Id> command. It has three bytes vendor id as
|
||||
* parameter.
|
||||
*
|
||||
* <p>This is a broadcast message sent to all devices on the bus.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param vendorId device's vendor id
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildDeviceVendorIdCommand(int src, int vendorId) {
|
||||
byte[] params = new byte[] {
|
||||
(byte) ((vendorId >> 16) & 0xFF),
|
||||
(byte) ((vendorId >> 8) & 0xFF),
|
||||
(byte) (vendorId & 0xFF)
|
||||
};
|
||||
// <Device Vendor Id> is broadcast message.
|
||||
return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_DEVICE_VENDOR_ID,
|
||||
params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build <Device Vendor Id> command. It has one byte cec version as parameter.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @param version version of cec. Use 0x04 for "Version 1.3a" and 0x05 for
|
||||
* "Version 1.4 or 1.4a or 1.4b
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
static HdmiCecMessage buildCecVersion(int src, int dest, int version) {
|
||||
byte[] params = new byte[] {
|
||||
(byte) version
|
||||
};
|
||||
return buildCommand(src, dest, HdmiCec.MESSAGE_CEC_VERSION, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link HdmiCecMessage} without extra parameter.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @param opcode opcode for a message
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
private static HdmiCecMessage buildCommand(int src, int dest, int opcode) {
|
||||
return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link HdmiCecMessage} with given values.
|
||||
*
|
||||
* @param src source address of command
|
||||
* @param dest destination address of command
|
||||
* @param opcode opcode for a message
|
||||
* @param params extra parameters for command
|
||||
* @return newly created {@link HdmiCecMessage}
|
||||
*/
|
||||
private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) {
|
||||
return new HdmiCecMessage(src, dest, opcode, params);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.hdmi.HdmiCec;
|
||||
import android.hardware.hdmi.HdmiCecDeviceInfo;
|
||||
import android.hardware.hdmi.HdmiCecMessage;
|
||||
import android.os.HandlerThread;
|
||||
@@ -26,6 +27,8 @@ import android.util.Slog;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Provides a service for sending and processing HDMI control messages,
|
||||
* HDMI-CEC and MHL control command, and providing the information on both standard.
|
||||
@@ -105,7 +108,7 @@ public final class HdmiControlService extends SystemService {
|
||||
* @param command CEC command to send out
|
||||
*/
|
||||
void sendCecCommand(HdmiCecMessage command) {
|
||||
// TODO: Implement this.
|
||||
mCecController.sendCommand(command);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,4 +119,89 @@ public final class HdmiControlService extends SystemService {
|
||||
void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
|
||||
// TODO: Implement this.
|
||||
}
|
||||
|
||||
boolean handleCecCommand(HdmiCecMessage message) {
|
||||
// Commands that queries system information replies directly instead
|
||||
// of creating FeatureAction because they are state-less.
|
||||
switch (message.getOpcode()) {
|
||||
case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
|
||||
handleGetMenuLanguage(message);
|
||||
return true;
|
||||
case HdmiCec.MESSAGE_GET_OSD_NAME:
|
||||
handleGetOsdName(message);
|
||||
return true;
|
||||
case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
|
||||
handleGivePhysicalAddress(message);
|
||||
return true;
|
||||
case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
|
||||
handleGiveDeviceVendorId(message);
|
||||
return true;
|
||||
case HdmiCec.MESSAGE_GET_CEC_VERSION:
|
||||
handleGetCecVersion(message);
|
||||
return true;
|
||||
// TODO: Add remaining system information query such as
|
||||
// <Give Device Power Status> and <Request Active Source> handler.
|
||||
default:
|
||||
Slog.w(TAG, "Unsupported cec command:" + message.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGetCecVersion(HdmiCecMessage message) {
|
||||
int version = mCecController.getVersion();
|
||||
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
|
||||
message.getSource(),
|
||||
version);
|
||||
sendCecCommand(cecMessage);
|
||||
}
|
||||
|
||||
private void handleGiveDeviceVendorId(HdmiCecMessage message) {
|
||||
int vendorId = mCecController.getVendorId();
|
||||
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
|
||||
message.getDestination(), vendorId);
|
||||
sendCecCommand(cecMessage);
|
||||
}
|
||||
|
||||
private void handleGivePhysicalAddress(HdmiCecMessage message) {
|
||||
int physicalAddress = mCecController.getPhysicalAddress();
|
||||
int deviceType = HdmiCec.getTypeFromAddress(message.getDestination());
|
||||
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
|
||||
message.getDestination(), physicalAddress, deviceType);
|
||||
sendCecCommand(cecMessage);
|
||||
}
|
||||
|
||||
private void handleGetOsdName(HdmiCecMessage message) {
|
||||
// TODO: read device name from settings or property.
|
||||
String name = HdmiCec.getDefaultDeviceName(message.getDestination());
|
||||
HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
|
||||
message.getDestination(), message.getSource(), name);
|
||||
if (cecMessage != null) {
|
||||
sendCecCommand(cecMessage);
|
||||
} else {
|
||||
Slog.w(TAG, "Failed to build <Get Osd Name>:" + name);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGetMenuLanguage(HdmiCecMessage message) {
|
||||
// Only 0 (TV), 14 (specific use) can answer.
|
||||
if (message.getDestination() != HdmiCec.ADDR_TV
|
||||
&& message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) {
|
||||
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
|
||||
sendCecCommand(
|
||||
HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
|
||||
message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
|
||||
HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE));
|
||||
return;
|
||||
}
|
||||
|
||||
HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
|
||||
message.getDestination(),
|
||||
Locale.getDefault().getISO3Language());
|
||||
// TODO: figure out how to handle failed to get language code.
|
||||
if (command != null) {
|
||||
sendCecCommand(command);
|
||||
} else {
|
||||
Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ final class NewDeviceAction extends FeatureAction {
|
||||
@Override
|
||||
public boolean start() {
|
||||
sendCommand(
|
||||
buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME));
|
||||
HdmiCecMessageBuilder.buildGetOsdNameCommand(mSourceAddress,
|
||||
mDeviceLogicalAddress));
|
||||
mState = STATE_WAITING_FOR_SET_OSD_NAME;
|
||||
addTimer(mState, TIMEOUT_MS);
|
||||
return true;
|
||||
@@ -132,8 +133,8 @@ final class NewDeviceAction extends FeatureAction {
|
||||
}
|
||||
|
||||
private void requestVendorId() {
|
||||
sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress,
|
||||
HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID));
|
||||
sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
|
||||
mDeviceLogicalAddress));
|
||||
addTimer(mState, TIMEOUT_MS);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user