diff options
| author | 2022-06-27 17:20:58 +0530 | |
|---|---|---|
| committer | 2022-09-29 02:22:45 +0530 | |
| commit | 40e3da0cd5c6c501bd40d504e95f00b7b97f0ae0 (patch) | |
| tree | 80c6f34be89e686a39354ee37e2d264d011e6769 | |
| parent | f55958fcfd3ce855455f38aed07325ebae2b2906 (diff) | |
CEC: AIDL Native Wrapper
Add native wrapper implementation for CEC AIDL interface.
Bug: 233026642
Test: Manual
Change-Id: I154a6dc8f51a8326c5010c58ff561f68814b68a3
4 files changed, 390 insertions, 49 deletions
diff --git a/services/core/Android.bp b/services/core/Android.bp index b00d72b16372..d35c07f1962a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -153,6 +153,8 @@ java_library_static { "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 1a568c30c899..bf0052d31826 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 @@ package com.android.server.hdmi; 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 @@ final class HdmiCecController { * 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 @@ final class HdmiCecController { } /** - * 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 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 enableWakeupByOtp(boolean enabled) { + assertRunOnServiceThread(); + 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 flag key of option - * @param enabled whether to enable/disable the given option. + * @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 setOption(int flag, boolean enabled) { + void enableSystemCecControl(boolean enabled) { assertRunOnServiceThread(); - HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled); - mNativeWrapperImpl.nativeSetOption(flag, enabled); + HdmiLogger.debug("enableSystemCecControl: %b", enabled); + mNativeWrapperImpl.enableSystemCecControl(enabled); } /** @@ -819,12 +857,233 @@ final class HdmiCecController { 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 @@ final class HdmiCecController { } } - @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 @@ final class HdmiCecController { } @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 @@ final class HdmiCecController { 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 @@ final class HdmiCecController { @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 @@ final class HdmiCecController { } } - @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 @@ final class HdmiCecController { } @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 @@ final class HdmiCecController { } } - 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 @@ final class HdmiCecController { } @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 @@ final class HdmiCecController { } @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 @@ final class HdmiCecController { } } + 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 f8a74f4f3f55..3256b49c0f4f 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.IHdmiMhlVendorCommandListener; 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 @@ public class HdmiControlService extends SystemService { 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 @@ public class HdmiControlService extends SystemService { @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 @@ public class HdmiControlService extends SystemService { mWakeUpMessageReceived = false; if (isTvDeviceEnabled()) { - mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup()); + mCecController.enableWakeupByOtp(tv().getAutoWakeup()); } int reason = -1; switch (initiatedBy) { @@ -988,7 +988,7 @@ public class HdmiControlService extends SystemService { 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 @@ public class HdmiControlService extends SystemService { 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 @@ public class HdmiControlService extends SystemService { } @ServiceThreadOnly - void setCecOption(int key, boolean value) { - assertRunOnServiceThread(); - mCecController.setOption(key, value); - } - - @ServiceThreadOnly void setControlEnabled(@HdmiControlManager.HdmiCecControl int enabled) { assertRunOnServiceThread(); @@ -3610,8 +3604,8 @@ public class HdmiControlService extends SystemService { @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 @@ public class HdmiControlService extends SystemService { @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 559a2c0d6a09..29eccd46650e 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 @@ final class FakeNativeWrapper implements NativeWrapper { } @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) {} |