CEC: AIDL Native Wrapper
Add native wrapper implementation for CEC AIDL interface.
Bug: 233026642
Test: Manual
Change-Id: I154a6dc8f51a8326c5010c58ff561f68814b68a3
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b00d72b..d35c07f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -153,6 +153,8 @@
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
+ "android.hardware.tv.cec-V1-java",
+ "android.hardware.tv.hdmi-V1-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 1a568c3..bf0052d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -19,20 +19,25 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.CecMessage;
+import android.hardware.tv.cec.IHdmiCec;
+import android.hardware.tv.cec.IHdmiCecCallback;
import android.hardware.tv.cec.V1_0.HotplugEvent;
-import android.hardware.tv.cec.V1_0.IHdmiCec;
import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
-import android.hardware.tv.cec.V1_0.IHdmiCecCallback;
+import android.hardware.tv.cec.V1_0.OptionKey;
import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.hardware.tv.hdmi.IHdmi;
+import android.hardware.tv.hdmi.IHdmiCallback;
import android.icu.util.IllformedLocaleException;
import android.icu.util.ULocale;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
@@ -168,8 +173,14 @@
* returns {@code null}.
*/
static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
- HdmiCecController controller = createWithNativeWrapper(service, new NativeWrapperImpl11(),
- atomWriter);
+ HdmiCecController controller =
+ createWithNativeWrapper(service, new NativeWrapperImplAidl(), atomWriter);
+ if (controller != null) {
+ return controller;
+ }
+ HdmiLogger.warning("Unable to use CEC and HDMI AIDL HALs");
+
+ controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter);
if (controller != null) {
return controller;
}
@@ -360,16 +371,43 @@
}
/**
- * Set an option to CEC HAL.
+ * Configures the TV panel device wakeup behaviour in standby mode when it receives an OTP
+ * (One Touch Play) from a source device.
*
- * @param flag key of option
- * @param enabled whether to enable/disable the given option.
+ * @param value If true, the TV device will wake up when OTP is received and if false, the TV
+ * device will not wake up for an OTP.
*/
@ServiceThreadOnly
- void setOption(int flag, boolean enabled) {
+ void enableWakeupByOtp(boolean enabled) {
assertRunOnServiceThread();
- HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
- mNativeWrapperImpl.nativeSetOption(flag, enabled);
+ HdmiLogger.debug("enableWakeupByOtp: %b", enabled);
+ mNativeWrapperImpl.enableWakeupByOtp(enabled);
+ }
+
+ /**
+ * Switch to enable or disable CEC on the device.
+ *
+ * @param value If true, the device will have all CEC functionalities and if false, the device
+ * will not perform any CEC functions.
+ */
+ @ServiceThreadOnly
+ void enableCec(boolean enabled) {
+ assertRunOnServiceThread();
+ HdmiLogger.debug("enableCec: %b", enabled);
+ mNativeWrapperImpl.enableCec(enabled);
+ }
+
+ /**
+ * Configures the module that processes CEC messages - the Android framework or the HAL.
+ *
+ * @param value If true, the Android framework will actively process CEC messages and if false,
+ * only the HAL will process the CEC messages.
+ */
+ @ServiceThreadOnly
+ void enableSystemCecControl(boolean enabled) {
+ assertRunOnServiceThread();
+ HdmiLogger.debug("enableSystemCecControl: %b", enabled);
+ mNativeWrapperImpl.enableSystemCecControl(enabled);
}
/**
@@ -819,12 +857,233 @@
int nativeGetVersion();
int nativeGetVendorId();
HdmiPortInfo[] nativeGetPortInfos();
- void nativeSetOption(int flag, boolean enabled);
+
+ void enableWakeupByOtp(boolean enabled);
+
+ void enableCec(boolean enabled);
+
+ void enableSystemCecControl(boolean enabled);
+
void nativeSetLanguage(String language);
void nativeEnableAudioReturnChannel(int port, boolean flag);
boolean nativeIsConnected(int port);
}
+ private static final class NativeWrapperImplAidl
+ implements NativeWrapper, IBinder.DeathRecipient {
+ private IHdmiCec mHdmiCec;
+ private IHdmi mHdmi;
+ @Nullable private HdmiCecCallback mCallback;
+
+ private final Object mLock = new Object();
+
+ @Override
+ public String nativeInit() {
+ return connectToHal() ? mHdmiCec.toString() + " " + mHdmi.toString() : null;
+ }
+
+ boolean connectToHal() {
+ mHdmiCec =
+ IHdmiCec.Stub.asInterface(
+ ServiceManager.getService(IHdmiCec.DESCRIPTOR + "/default"));
+ if (mHdmiCec == null) {
+ HdmiLogger.error("Could not initialize HDMI CEC AIDL HAL");
+ return false;
+ }
+ try {
+ mHdmiCec.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't link to death : ", e);
+ }
+
+ mHdmi =
+ IHdmi.Stub.asInterface(
+ ServiceManager.getService(IHdmi.DESCRIPTOR + "/default"));
+ if (mHdmi == null) {
+ HdmiLogger.error("Could not initialize HDMI AIDL HAL");
+ return false;
+ }
+ try {
+ mHdmi.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't link to death : ", e);
+ }
+ return true;
+ }
+
+ @Override
+ public void binderDied() {
+ // One of the services died, try to reconnect to both.
+ mHdmiCec.asBinder().unlinkToDeath(this, 0);
+ mHdmi.asBinder().unlinkToDeath(this, 0);
+ HdmiLogger.error("HDMI or CEC service died, reconnecting");
+ connectToHal();
+ // Reconnect the callback
+ if (mCallback != null) {
+ setCallback(mCallback);
+ }
+ }
+
+ @Override
+ public void setCallback(HdmiCecCallback callback) {
+ mCallback = callback;
+ try {
+ // Create an AIDL callback that can callback onCecMessage
+ mHdmiCec.setCallback(new HdmiCecCallbackAidl(callback));
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
+ }
+ try {
+ // Create an AIDL callback that can callback onHotplugEvent
+ mHdmi.setCallback(new HdmiCallbackAidl(callback));
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e);
+ }
+ }
+
+ @Override
+ public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
+ CecMessage message = new CecMessage();
+ message.initiator = (byte) (srcAddress & 0xF);
+ message.destination = (byte) (dstAddress & 0xF);
+ message.body = body;
+ try {
+ return mHdmiCec.sendMessage(message);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to send CEC message : ", e);
+ return SendMessageResult.FAIL;
+ }
+ }
+
+ @Override
+ public int nativeAddLogicalAddress(int logicalAddress) {
+ try {
+ return mHdmiCec.addLogicalAddress((byte) logicalAddress);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to add a logical address : ", e);
+ return Result.FAILURE_INVALID_ARGS;
+ }
+ }
+
+ @Override
+ public void nativeClearLogicalAddress() {
+ try {
+ mHdmiCec.clearLogicalAddress();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to clear logical address : ", e);
+ }
+ }
+
+ @Override
+ public int nativeGetPhysicalAddress() {
+ try {
+ return mHdmiCec.getPhysicalAddress();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get physical address : ", e);
+ return INVALID_PHYSICAL_ADDRESS;
+ }
+ }
+
+ @Override
+ public int nativeGetVersion() {
+ try {
+ return mHdmiCec.getCecVersion();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get cec version : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
+ }
+
+ @Override
+ public int nativeGetVendorId() {
+ try {
+ return mHdmiCec.getVendorId();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get vendor id : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
+ }
+
+ @Override
+ public void enableWakeupByOtp(boolean enabled) {
+ try {
+ mHdmiCec.enableWakeupByOtp(enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed call to enableWakeupByOtp : ", e);
+ }
+ }
+
+ @Override
+ public void enableCec(boolean enabled) {
+ try {
+ mHdmiCec.enableCec(enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed call to enableCec : ", e);
+ }
+ }
+
+ @Override
+ public void enableSystemCecControl(boolean enabled) {
+ try {
+ mHdmiCec.enableSystemCecControl(enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed call to enableSystemCecControl : ", e);
+ }
+ }
+
+ @Override
+ public void nativeSetLanguage(String language) {
+ try {
+ mHdmiCec.setLanguage(language);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set language : ", e);
+ }
+ }
+
+ @Override
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {
+ try {
+ mHdmiCec.enableAudioReturnChannel(port, flag);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to enable/disable ARC : ", e);
+ }
+ }
+
+ @Override
+ public HdmiPortInfo[] nativeGetPortInfos() {
+ try {
+ android.hardware.tv.hdmi.HdmiPortInfo[] hdmiPortInfos = mHdmi.getPortInfo();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
+ int i = 0;
+ for (android.hardware.tv.hdmi.HdmiPortInfo portInfo : hdmiPortInfos) {
+ hdmiPortInfo[i] =
+ new HdmiPortInfo(
+ portInfo.portId,
+ portInfo.type,
+ portInfo.physicalAddress,
+ portInfo.cecSupported,
+ false,
+ portInfo.arcSupported);
+ i++;
+ }
+ return hdmiPortInfo;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get port information : ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean nativeIsConnected(int port) {
+ try {
+ return mHdmi.isConnected(port);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get connection info : ", e);
+ return false;
+ }
+ }
+ }
+
private static final class NativeWrapperImpl11 implements NativeWrapper,
IHwBinder.DeathRecipient, getPhysicalAddressCallback {
private android.hardware.tv.cec.V1_1.IHdmiCec mHdmiCec;
@@ -975,8 +1234,7 @@
}
}
- @Override
- public void nativeSetOption(int flag, boolean enabled) {
+ private void nativeSetOption(int flag, boolean enabled) {
try {
mHdmiCec.setOption(flag, enabled);
} catch (RemoteException e) {
@@ -985,6 +1243,21 @@
}
@Override
+ public void enableWakeupByOtp(boolean enabled) {
+ nativeSetOption(OptionKey.WAKEUP, enabled);
+ }
+
+ @Override
+ public void enableCec(boolean enabled) {
+ nativeSetOption(OptionKey.ENABLE_CEC, enabled);
+ }
+
+ @Override
+ public void enableSystemCecControl(boolean enabled) {
+ nativeSetOption(OptionKey.SYSTEM_CEC_CONTROL, enabled);
+ }
+
+ @Override
public void nativeSetLanguage(String language) {
try {
mHdmiCec.setLanguage(language);
@@ -1028,7 +1301,7 @@
boolean connectToHal() {
try {
- mHdmiCec = IHdmiCec.getService(true);
+ mHdmiCec = android.hardware.tv.cec.V1_0.IHdmiCec.getService(true);
try {
mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
} catch (RemoteException e) {
@@ -1053,7 +1326,8 @@
@Override
public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
- CecMessage message = new CecMessage();
+ android.hardware.tv.cec.V1_0.CecMessage message =
+ new android.hardware.tv.cec.V1_0.CecMessage();
message.initiator = srcAddress;
message.destination = dstAddress;
message.body = new ArrayList<>(body.length);
@@ -1141,8 +1415,7 @@
}
}
- @Override
- public void nativeSetOption(int flag, boolean enabled) {
+ private void nativeSetOption(int flag, boolean enabled) {
try {
mHdmiCec.setOption(flag, enabled);
} catch (RemoteException e) {
@@ -1151,6 +1424,21 @@
}
@Override
+ public void enableWakeupByOtp(boolean enabled) {
+ nativeSetOption(OptionKey.WAKEUP, enabled);
+ }
+
+ @Override
+ public void enableCec(boolean enabled) {
+ nativeSetOption(OptionKey.ENABLE_CEC, enabled);
+ }
+
+ @Override
+ public void enableSystemCecControl(boolean enabled) {
+ nativeSetOption(OptionKey.SYSTEM_CEC_CONTROL, enabled);
+ }
+
+ @Override
public void nativeSetLanguage(String language) {
try {
mHdmiCec.setLanguage(language);
@@ -1211,7 +1499,8 @@
}
}
- private static final class HdmiCecCallback10 extends IHdmiCecCallback.Stub {
+ private static final class HdmiCecCallback10
+ extends android.hardware.tv.cec.V1_0.IHdmiCecCallback.Stub {
private final HdmiCecCallback mHdmiCecCallback;
HdmiCecCallback10(HdmiCecCallback hdmiCecCallback) {
@@ -1219,7 +1508,8 @@
}
@Override
- public void onCecMessage(CecMessage message) throws RemoteException {
+ public void onCecMessage(android.hardware.tv.cec.V1_0.CecMessage message)
+ throws RemoteException {
byte[] body = new byte[message.body.size()];
for (int i = 0; i < message.body.size(); i++) {
body[i] = message.body.get(i);
@@ -1252,7 +1542,8 @@
}
@Override
- public void onCecMessage(CecMessage message) throws RemoteException {
+ public void onCecMessage(android.hardware.tv.cec.V1_0.CecMessage message)
+ throws RemoteException {
byte[] body = new byte[message.body.size()];
for (int i = 0; i < message.body.size(); i++) {
body[i] = message.body.get(i);
@@ -1266,6 +1557,52 @@
}
}
+ private static final class HdmiCecCallbackAidl extends IHdmiCecCallback.Stub {
+ private final HdmiCecCallback mHdmiCecCallback;
+
+ HdmiCecCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+ mHdmiCecCallback = hdmiCecCallback;
+ }
+
+ @Override
+ public void onCecMessage(CecMessage message) throws RemoteException {
+ mHdmiCecCallback.onCecMessage(message.initiator, message.destination, message.body);
+ }
+
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ return IHdmiCecCallback.Stub.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() throws android.os.RemoteException {
+ return IHdmiCecCallback.Stub.VERSION;
+ }
+ }
+
+ private static final class HdmiCallbackAidl extends IHdmiCallback.Stub {
+ private final HdmiCecCallback mHdmiCecCallback;
+
+ HdmiCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+ mHdmiCecCallback = hdmiCecCallback;
+ }
+
+ @Override
+ public void onHotplugEvent(boolean connected, int portId) throws RemoteException {
+ mHdmiCecCallback.onHotplugEvent(portId, connected);
+ }
+
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ return IHdmiCallback.Stub.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() throws android.os.RemoteException {
+ return IHdmiCallback.Stub.VERSION;
+ }
+ }
+
public abstract static class Dumpable {
protected final long mTime;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index f8a74f4..3256b49 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,7 +56,6 @@
import android.hardware.hdmi.IHdmiRecordListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
-import android.hardware.tv.cec.V1_0.OptionKey;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
@@ -656,7 +655,7 @@
if (mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
initializeCec(INITIATED_BY_BOOT_UP);
} else {
- mCecController.setOption(OptionKey.ENABLE_CEC, false);
+ mCecController.enableCec(false);
}
mMhlDevices = Collections.emptyList();
@@ -730,10 +729,11 @@
@Override
public void onChange(String setting) {
if (isTvDeviceEnabled()) {
- setCecOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+ mCecController.enableWakeupByOtp(tv().getAutoWakeup());
}
}
- }, mServiceThreadExecutor);
+ },
+ mServiceThreadExecutor);
}
/** Returns true if the device screen is off */
@@ -854,7 +854,7 @@
mWakeUpMessageReceived = false;
if (isTvDeviceEnabled()) {
- mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+ mCecController.enableWakeupByOtp(tv().getAutoWakeup());
}
int reason = -1;
switch (initiatedBy) {
@@ -988,7 +988,7 @@
mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
Math.min(settingsCecVersion, supportedCecVersion));
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
+ mCecController.enableSystemCecControl(true);
mCecController.setLanguage(mMenuLanguage);
initializeLocalDevices(initiatedBy);
}
@@ -3422,7 +3422,7 @@
device.onStandby(mStandbyMessageReceived, standbyAction);
}
if (!isAudioSystemDevice()) {
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
+ mCecController.enableSystemCecControl(false);
mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
}
}
@@ -3571,12 +3571,6 @@
}
@ServiceThreadOnly
- void setCecOption(int key, boolean value) {
- assertRunOnServiceThread();
- mCecController.setOption(key, value);
- }
-
- @ServiceThreadOnly
void setControlEnabled(@HdmiControlManager.HdmiCecControl int enabled) {
assertRunOnServiceThread();
@@ -3610,8 +3604,8 @@
@ServiceThreadOnly
private void enableHdmiControlService() {
- mCecController.setOption(OptionKey.ENABLE_CEC, true);
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
+ mCecController.enableCec(true);
+ mCecController.enableSystemCecControl(true);
mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
initializeCec(INITIATED_BY_ENABLE_CEC);
@@ -3619,21 +3613,23 @@
@ServiceThreadOnly
private void disableHdmiControlService() {
- disableDevices(new PendingActionClearedCallback() {
- @Override
- public void onCleared(HdmiCecLocalDevice device) {
- assertRunOnServiceThread();
- mCecController.flush(new Runnable() {
+ disableDevices(
+ new PendingActionClearedCallback() {
@Override
- public void run() {
- mCecController.setOption(OptionKey.ENABLE_CEC, false);
- mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
- mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
- clearLocalDevices();
+ public void onCleared(HdmiCecLocalDevice device) {
+ assertRunOnServiceThread();
+ mCecController.flush(
+ new Runnable() {
+ @Override
+ public void run() {
+ mCecController.enableCec(false);
+ mCecController.enableSystemCecControl(false);
+ mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
+ clearLocalDevices();
+ }
+ });
}
});
- }
- });
}
@ServiceThreadOnly
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 559a2c0..29eccd4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -118,7 +118,13 @@
}
@Override
- public void nativeSetOption(int flag, boolean enabled) {}
+ public void enableWakeupByOtp(boolean enabled) {}
+
+ @Override
+ public void enableCec(boolean enabled) {}
+
+ @Override
+ public void enableSystemCecControl(boolean enabled) {}
@Override
public void nativeSetLanguage(String language) {}