Loading services/core/java/com/android/server/hdmi/FeatureAction.java +0 −33 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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) }; } } services/core/java/com/android/server/hdmi/HdmiCecController.java +28 −20 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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); } Loading services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java 0 → 100644 +212 −0 Original line number Diff line number Diff line /* * 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); } } services/core/java/com/android/server/hdmi/HdmiControlService.java +89 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -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); } /** Loading @@ -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()); } } } services/core/java/com/android/server/hdmi/NewDeviceAction.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading Loading
services/core/java/com/android/server/hdmi/FeatureAction.java +0 −33 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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) }; } }
services/core/java/com/android/server/hdmi/HdmiCecController.java +28 −20 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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() { Loading Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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); } Loading
services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java 0 → 100644 +212 −0 Original line number Diff line number Diff line /* * 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); } }
services/core/java/com/android/server/hdmi/HdmiControlService.java +89 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -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); } /** Loading @@ -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()); } } }
services/core/java/com/android/server/hdmi/NewDeviceAction.java +4 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading