Merge "Move message handling logic to local device instead of service."
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 6f42c8b..d36fc2c 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -259,7 +259,7 @@
byte params[] = cmd.getParams();
if (params.length == 3) {
- current.mPhysicalAddress = ((params[0] & 0xFF) << 8) | (params[1] & 0xFF);
+ current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
current.mDeviceType = params[2] & 0xFF;
increaseProcessedDeviceCount();
@@ -307,9 +307,7 @@
byte[] params = cmd.getParams();
if (params.length == 3) {
- int vendorId = ((params[0] & 0xFF) << 16)
- | ((params[1] & 0xFF) << 8)
- | (params[2] & 0xFF);
+ int vendorId = HdmiUtils.threeBytesToInt(params);
current.mVendorId = vendorId;
} else {
Slog.w(TAG, "Invalid vendor id: " + cmd.toString());
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5c420d7..f869424 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -387,6 +387,41 @@
}
/**
+ * Pass a option to CEC HAL.
+ *
+ * @param flag a key of option. For more details, look at
+ * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
+ * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
+ * @param value a value of option. Actual value varies flag. For more
+ * details, look at description of flags
+ */
+ void setOption(int flag, int value) {
+ assertRunOnServiceThread();
+ nativeSetOption(mNativePtr, flag, value);
+ }
+
+ /**
+ * Configure ARC circuit in the hardware logic to start or stop the feature.
+ *
+ * @param enabled whether to enable/disable ARC
+ */
+ void setAudioReturnChannel(boolean enabled) {
+ assertRunOnServiceThread();
+ nativeSetAudioReturnChannel(mNativePtr, enabled);
+ }
+
+ /**
+ * Return the connection status of the specified port
+ *
+ * @param port port number to check connection status
+ * @return true if connected; otherwise, return false
+ */
+ boolean isConnected(int port) {
+ assertRunOnServiceThread();
+ return nativeIsConnected(mNativePtr, port);
+ }
+
+ /**
* Poll all remote devices. It sends <Polling Message> to all remote
* devices.
*
@@ -606,4 +641,8 @@
private static native int nativeGetPhysicalAddress(long controllerPtr);
private static native int nativeGetVersion(long controllerPtr);
private static native int nativeGetVendorId(long controllerPtr);
+ private static native void nativeSetOption(long controllerPtr, int flag, int value);
+ private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
+ private static native boolean nativeIsConnected(long controllerPtr, int port);
+
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 23454ad..6697a53 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -18,12 +18,15 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
*/
abstract class HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevice";
protected final HdmiControlService mService;
protected final int mDeviceType;
@@ -59,6 +62,83 @@
*/
protected abstract void onAddressAllocated(int logicalAddress);
+ /**
+ * Dispatch incoming message.
+ *
+ * @param message incoming message
+ * @return true if consumed a message; otherwise, return false.
+ */
+ final boolean dispatchMessage(HdmiCecMessage message) {
+ int dest = message.getDestination();
+ if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
+ return false;
+ }
+ return onMessage(message);
+ }
+
+ protected boolean onMessage(HdmiCecMessage message) {
+ switch (message.getOpcode()) {
+ case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
+ return handleGetMenuLanguage(message);
+ case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
+ return handleGivePhysicalAddress();
+ case HdmiCec.MESSAGE_GIVE_OSD_NAME:
+ return handleGiveOsdName(message);
+ case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
+ return handleGiveDeviceVendorId();
+ case HdmiCec.MESSAGE_GET_CEC_VERSION:
+ return handleGetCecVersion(message);
+ default:
+ return false;
+ }
+ }
+
+ protected boolean handleGivePhysicalAddress() {
+ int physicalAddress = mService.getPhysicalAddress();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, physicalAddress, mDeviceType);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGiveDeviceVendorId() {
+ int vendorId = mService.getVendorId();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, vendorId);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ int version = mService.getCecVersion();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
+ message.getSource(), version);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+ message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
+ HdmiConstants.ABORT_UNRECOGNIZED_MODE));
+ return true;
+ }
+
+ protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ // Note that since this method is called after logical address allocation is done,
+ // mDeviceInfo should not be null.
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ mAddress, message.getSource(), mDeviceInfo.getDisplayName());
+ if (cecMessage != null) {
+ mService.sendCecCommand(cecMessage);
+ } else {
+ Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
+ }
+ return true;
+ }
+
final void handleAddressAllocated(int logicalAddress) {
mAddress = mPreferredAddress = logicalAddress;
onAddressAllocated(logicalAddress);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 72d7f2d..8bd81ea 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -17,11 +17,16 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import java.util.Locale;
/**
* Represent a logical device of type TV residing in Android system.
*/
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDeviceTv";
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiCec.DEVICE_TV);
@@ -39,4 +44,42 @@
mService.launchDeviceDiscovery(mAddress);
// TODO: Start routing control action, device discovery action.
}
+
+ @Override
+ protected boolean onMessage(HdmiCecMessage message) {
+ switch (message.getOpcode()) {
+ case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ return handleReportPhysicalAddress(message);
+ default:
+ return super.onMessage(message);
+ }
+ }
+
+ @Override
+ protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
+ mAddress, Locale.getDefault().getISO3Language());
+ // TODO: figure out how to handle failed to get language code.
+ if (command != null) {
+ mService.sendCecCommand(command);
+ } else {
+ Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
+ }
+ return true;
+ }
+
+ private boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+ // Ignore if [Device Discovery Action] is going on.
+ if (mService.hasAction(DeviceDiscoveryAction.class)) {
+ Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ + "because Device Discovery Action is on-going:" + message);
+ return true;
+ }
+
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ mService.addAndStartAction(new NewDeviceAction(mService,
+ mAddress, message.getSource(), physicalAddress));
+
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index a83d1ed..54b5dcb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -43,5 +43,31 @@
static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+ // Flags used for setOption to CEC HAL.
+ /**
+ * When set to false, HAL does not wake up the system upon receiving
+ * <Image View On> or <Text View On>. Used when user changes the TV
+ * settings to disable the auto TV on functionality.
+ * True by default.
+ */
+ static final int FLAG_HDMI_OPTION_WAKEUP = 1;
+ /**
+ * When set to false, all the CEC commands are discarded. Used when
+ * user changes the TV settings to disable CEC functionality.
+ * True by default.
+ */
+ static final int FLAG_HDMI_OPTION_ENABLE_CEC = 2;
+ /**
+ * Setting this flag to false means Android system will stop handling
+ * CEC service and yield the control over to the microprocessor that is
+ * powered on through the standby mode. When set to true, the system
+ * will gain the control over, hence telling the microprocessor to stop
+ * handling the cec commands. This is called when system goes
+ * in and out of standby mode to notify the microprocessor that it should
+ * start/stop handling CEC commands on behalf of the system.
+ * False by default.
+ */
+ static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3;
+
private HdmiConstants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 58bd5f1..3e0f07a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -239,6 +239,13 @@
}
/**
+ * Returns version of CEC.
+ */
+ int getCecVersion() {
+ return mCecController.getVersion();
+ }
+
+ /**
* Returns a list of {@link HdmiCecDeviceInfo}.
*
* @param includeLocalDevice whether to include local devices
@@ -277,7 +284,7 @@
}
// See if we have an action of a given type in progress.
- private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
for (FeatureAction action : mActions) {
if (action.getClass().equals(clazz)) {
return true;
@@ -335,10 +342,13 @@
* @return {@code true} if ARC was in "Enabled" status
*/
boolean setArcStatus(boolean enabled) {
+ assertRunOnServiceThread();
synchronized (mLock) {
boolean oldStatus = mArcStatusEnabled;
// 1. Enable/disable ARC circuit.
- // TODO: call set_audio_return_channel of hal interface.
+ mCecController.setAudioReturnChannel(enabled);
+
+ // TODO: notify arc mode change to AudioManager.
// 2. Update arc status;
mArcStatusEnabled = enabled;
@@ -366,31 +376,14 @@
// Commands that queries system information replies directly instead
// of creating FeatureAction because they are state-less.
+ // TODO: move the leftover message to local device.
switch (message.getOpcode()) {
- case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
- handleGetMenuLanguage(message);
- return true;
- case HdmiCec.MESSAGE_GIVE_OSD_NAME:
- handleGiveOsdName(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;
case HdmiCec.MESSAGE_INITIATE_ARC:
handleInitiateArc(message);
return true;
case HdmiCec.MESSAGE_TERMINATE_ARC:
handleTerminateArc(message);
return true;
- case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
- handleReportPhysicalAddress(message);
- return true;
case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
handleSetSystemAudioMode(message);
return true;
@@ -398,8 +391,22 @@
handleSystemAudioModeStatus(message);
return true;
default:
- return dispatchMessageToAction(message);
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
+ break;
}
+
+ return dispatchMessageToLocalDevice(message);
+ }
+
+ private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ if (device.dispatchMessage(message)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -446,9 +453,8 @@
void launchDeviceDiscovery(final int sourceAddress) {
// At first, clear all existing device infos.
mCecController.clearDeviceInfoList();
- mCecMessageCache.flushAll();
+ // TODO: flush cec message cache when CEC is turned off.
- // TODO: check whether TV is one of local devices.
DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
new DeviceDiscoveryCallback() {
@Override
@@ -476,22 +482,6 @@
getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
- private void handleReportPhysicalAddress(HdmiCecMessage message) {
- // At first, try to consume it.
- if (dispatchMessageToAction(message)) {
- return;
- }
-
- // Ignore if [Device Discovery Action] is going on.
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
- + "because Device Discovery Action is on-going:" + message);
- return;
- }
-
- // TODO: start new device action.
- }
-
private void handleInitiateArc(HdmiCecMessage message){
// In case where <Initiate Arc> is started by <Request ARC Initiation>
// need to clean up RequestArcInitiationAction.
@@ -512,64 +502,6 @@
addAndStartAction(action);
}
- 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 handleGiveOsdName(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,
- HdmiConstants.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());
- }
- }
-
private boolean dispatchMessageToAction(HdmiCecMessage message) {
for (FeatureAction action : mActions) {
if (action.processCommand(message)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index ef128ed1..ca09fe6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -71,4 +71,24 @@
return cmd.getParams().length > 0
&& cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
}
+
+ /**
+ * Assemble two bytes into single integer value.
+ *
+ * @param data to be assembled
+ * @return assembled value
+ */
+ static int twoBytesToInt(byte[] data) {
+ return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
+ }
+
+ /**
+ * Assemble three bytes into single integer value.
+ *
+ * @param data to be assembled
+ * @return assembled value
+ */
+ static int threeBytesToInt(byte[] data) {
+ return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 088c40b..c284d10 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -114,8 +114,7 @@
} else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
if (params.length == 3) {
- mVendorId = ((params[0] & 0xFF) << 16) + ((params[1] & 0xFF) << 8)
- + (params[2] & 0xFF);
+ mVendorId = HdmiUtils.threeBytesToInt(params);
} else {
Slog.e(TAG, "Failed to get device vendor ID: ");
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index a734026..cbc853d 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -57,6 +57,12 @@
int getVersion();
// Get vendor id used for vendor command.
uint32_t getVendorId();
+ // Set a flag and its value.
+ void setOption(int flag, int value);
+ // Set audio return channel status.
+ void setAudioReturnChannel(bool flag);
+ // Whether to hdmi device is connected to the given port.
+ bool isConnected(int port);
jobject getCallbacksObj() const {
return mCallbacksObj;
@@ -222,6 +228,20 @@
return vendorId;
}
+void HdmiCecController::setOption(int flag, int value) {
+ mDevice->set_option(mDevice, flag, value);
+}
+
+// Set audio return channel status.
+void HdmiCecController::setAudioReturnChannel(bool enabled) {
+ mDevice->set_audio_return_channel(mDevice, enabled ? 1 : 0);
+}
+
+// Whether to hdmi device is connected to the given port.
+bool HdmiCecController::isConnected(int port) {
+ return mDevice->is_connected(mDevice, port) == HDMI_CONNECTED;
+}
+
// static
void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
@@ -326,6 +346,26 @@
return controller->getVendorId();
}
+static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag,
+ jint value) {
+ HdmiCecController* controller =
+ reinterpret_cast<HdmiCecController*>(controllerPtr);
+ controller->setOption(flag, value);
+}
+
+static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr,
+ jboolean enabled) {
+ HdmiCecController* controller =
+ reinterpret_cast<HdmiCecController*>(controllerPtr);
+ controller->setAudioReturnChannel(enabled == JNI_TRUE);
+}
+
+static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) {
+ HdmiCecController* controller =
+ reinterpret_cast<HdmiCecController*>(controllerPtr);
+ return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
@@ -337,6 +377,9 @@
{ "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress },
{ "nativeGetVersion", "(J)I", (void *) nativeGetVersion },
{ "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId },
+ { "nativeSetOption", "(JII)V", (void *) nativeSetOption },
+ { "nativeSetAudioReturnChannel", "(JZ)V", (void *) nativeSetAudioReturnChannel },
+ { "nativeIsConnected", "(JI)Z", (void *) nativeIsConnected },
};
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"