From 0608b9328b1c2f804ffb2d4165c34383d34bde2a Mon Sep 17 00:00:00 2001 From: Yuncheol Heo Date: Mon, 13 Oct 2014 16:39:18 +0900 Subject: [PATCH] CEC: Add a callback for vendor when HDMI control setting is changed. - removed unregisterContentObserver() to reactivate the service later. - added the parameter destAddress to onReceived() callback to distinguish whether the message is broadcast or not. Bug: 17962624 Change-Id: I552d14661583f63bb66b07866092f972b259b15a --- .../android/hardware/hdmi/HdmiClient.java | 10 ++- .../hardware/hdmi/HdmiControlManager.java | 29 ++++++++- .../hdmi/IHdmiVendorCommandListener.aidl | 3 +- .../server/hdmi/HdmiCecLocalDevice.java | 8 +-- .../server/hdmi/HdmiControlService.java | 62 ++++++++++++++----- 5 files changed, 89 insertions(+), 23 deletions(-) diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java index aba90e47dd9c4..c2b9846c0cdd8 100644 --- a/core/java/android/hardware/hdmi/HdmiClient.java +++ b/core/java/android/hardware/hdmi/HdmiClient.java @@ -3,7 +3,6 @@ package android.hardware.hdmi; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.hdmi.HdmiControlManager.VendorCommandListener; -import android.hardware.hdmi.IHdmiVendorCommandListener; import android.os.RemoteException; import android.util.Log; @@ -91,8 +90,13 @@ public abstract class HdmiClient { final VendorCommandListener listener) { return new IHdmiVendorCommandListener.Stub() { @Override - public void onReceived(int srcAddress, byte[] params, boolean hasVendorId) { - listener.onReceived(srcAddress, params, hasVendorId); + public void onReceived(int srcAddress, int destAddress, byte[] params, + boolean hasVendorId) { + listener.onReceived(srcAddress, destAddress, params, hasVendorId); + } + @Override + public void onControlStateChanged(boolean enabled, int reason) { + listener.onControlStateChanged(enabled, reason); } }; } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 30f3576648c1c..ff2ba1eec20fb 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -236,6 +236,15 @@ public final class HdmiControlManager { /** Clear timer error - CEC is disabled. */ public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2; + /** The HdmiControlService is started. */ + public static final int CONTROL_STATE_CHANGED_REASON_START = 0; + /** The state of HdmiControlService is changed by changing of settings. */ + public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; + /** The HdmiControlService is enabled to wake up. */ + public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; + /** The HdmiControlService will be disabled to standby. */ + public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; + // True if we have a logical device of type playback hosted in the system. private final boolean mHasPlaybackDevice; // True if we have a logical device of type TV hosted in the system. @@ -339,11 +348,29 @@ public final class HdmiControlManager { * Called when a vendor command is received. * * @param srcAddress source logical address + * @param destAddress destination logical address * @param params vendor-specific parameters * @param hasVendorId {@code true} if the command is <Vendor Command * With ID>. The first 3 bytes of params is vendor id. */ - void onReceived(int srcAddress, byte[] params, boolean hasVendorId); + void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId); + + /** + * The callback is called: + * + * The client shouldn't hold the thread too long since this is a blocking call. + * + * @param enabled {@code true} if HdmiControlService is enabled. + * @param reason the reason code why the state of HdmiControlService is changed. + * @see #CONTROL_STATE_CHANGED_REASON_START + * @see #CONTROL_STATE_CHANGED_REASON_SETTING + * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP + * @see #CONTROL_STATE_CHANGED_REASON_STANDBY + */ + void onControlStateChanged(boolean enabled, int reason); } /** diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl index 55cc9257667a0..a16e8787df8a1 100644 --- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl @@ -23,5 +23,6 @@ package android.hardware.hdmi; * @hide */ oneway interface IHdmiVendorCommandListener { - void onReceived(int logicalAddress, in byte[] operands, boolean hasVendorId); + void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId); + void onControlStateChanged(boolean enabled, int reason); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 836a46334e70b..4f8b9fbc71f5d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -516,8 +516,8 @@ abstract class HdmiCecLocalDevice { } protected boolean handleVendorCommand(HdmiCecMessage message) { - if (!mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), - message.getParams(), false)) { + if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(), + message.getDestination(), message.getParams(), false)) { // Vendor command listener may not have been registered yet. Respond with // [NOT_IN_CORRECT_MODE] so that the sender can try again later. mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); @@ -529,8 +529,8 @@ abstract class HdmiCecLocalDevice { byte[] params = message.getParams(); int vendorId = HdmiUtils.threeBytesToInt(params); if (vendorId == mService.getVendorId()) { - if (!mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, - true)) { + if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(), + message.getDestination(), params, true)) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); } } else if (message.getDestination() != Constants.ADDR_BROADCAST && diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d58d787eca178..8a8011ded13b4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -316,20 +316,23 @@ public final class HdmiControlService extends SystemService { mMessageValidator = new HdmiCecMessageValidator(this); publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); - // Register broadcast receiver for power state change. if (mCecController != null) { + // Register broadcast receiver for power state change. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter); + + // Register ContentObserver to monitor the settings change. + registerContentObserver(); } } /** * Called when the initialization of local devices is complete. */ - private void onInitializeCecComplete() { + private void onInitializeCecComplete(int initiatedBy) { if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { mPowerStatus = HdmiControlManager.POWER_STATUS_ON; } @@ -337,7 +340,22 @@ public final class HdmiControlService extends SystemService { if (isTvDevice()) { mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup())); - registerContentObserver(); + } + int reason = -1; + switch (initiatedBy) { + case INITIATED_BY_BOOT_UP: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START; + break; + case INITIATED_BY_ENABLE_CEC: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING; + break; + case INITIATED_BY_SCREEN_ON: + case INITIATED_BY_WAKE_UP_MESSAGE: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP; + break; + } + if (reason != -1) { + invokeVendorCommandListenersOnControlStateChanged(true, reason); } } @@ -402,10 +420,6 @@ public final class HdmiControlService extends SystemService { Global.putInt(cr, key, toInt(value)); } - private void unregisterSettingsObserver() { - getContext().getContentResolver().unregisterContentObserver(mSettingsObserver); - } - private void initializeCec(int initiatedBy) { mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); initializeLocalDevices(initiatedBy); @@ -460,7 +474,7 @@ public final class HdmiControlService extends SystemService { if (initiatedBy != INITIATED_BY_HOTPLUG) { // In case of the hotplug we don't call onInitializeCecComplete() // since we reallocate the logical address only. - onInitializeCecComplete(); + onInitializeCecComplete(initiatedBy); } notifyAddressAllocated(allocatedDevices, initiatedBy); } @@ -1789,6 +1803,8 @@ public final class HdmiControlService extends SystemService { private void onStandby() { assertRunOnServiceThread(); mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + invokeVendorCommandListenersOnControlStateChanged(false, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY); final List devices = getAllLocalDevices(); disableDevices(new PendingActionClearedCallback() { @@ -1827,9 +1843,6 @@ public final class HdmiControlService extends SystemService { for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { device.disableDevice(mStandbyMessageReceived, callback); } - if (isTvDevice()) { - unregisterSettingsObserver(); - } } mMhlController.clearAllLocalDevices(); @@ -1874,8 +1887,8 @@ public final class HdmiControlService extends SystemService { } } - boolean invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params, - boolean hasVendorId) { + boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress, + byte[] params, boolean hasVendorId) { synchronized (mLock) { if (mVendorCommandListenerRecords.isEmpty()) { return false; @@ -1885,7 +1898,7 @@ public final class HdmiControlService extends SystemService { continue; } try { - record.mListener.onReceived(srcAddress, params, hasVendorId); + record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId); } catch (RemoteException e) { Slog.e(TAG, "Failed to notify vendor command reception", e); } @@ -1894,6 +1907,22 @@ public final class HdmiControlService extends SystemService { } } + boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) { + synchronized (mLock) { + if (mVendorCommandListenerRecords.isEmpty()) { + return false; + } + for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { + try { + record.mListener.onControlStateChanged(enabled, reason); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e); + } + } + return true; + } + } + private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) { HdmiMhlVendorCommandListenerRecord record = new HdmiMhlVendorCommandListenerRecord(listener); @@ -1943,6 +1972,11 @@ public final class HdmiControlService extends SystemService { void setControlEnabled(boolean enabled) { assertRunOnServiceThread(); + if (!enabled) { + // Call the vendor handler before the service is disabled. + invokeVendorCommandListenersOnControlStateChanged(false, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING); + } int value = toInt(enabled); mCecController.setOption(OPTION_CEC_ENABLE, value); mMhlController.setOption(OPTION_MHL_ENABLE, value);