diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 2579ee687827a..da4d8e06ad480 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -24,6 +24,7 @@ import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -39,6 +40,8 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ConcurrentUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -318,6 +321,37 @@ public final class HdmiControlManager { /** The HdmiControlService will be disabled to standby. */ public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; + // -- Which devices the playback device can send a message to upon going to sleep. + /** + * Send to TV only. + * + * @hide + */ + public static final String SEND_STANDBY_ON_SLEEP_TO_TV = "to_tv"; + /** + * Broadcast to all devices in the network. + * + * @hide + */ + public static final String SEND_STANDBY_ON_SLEEP_BROADCAST = "broadcast"; + /** + * Don't send any message. + * + * @hide + */ + public static final String SEND_STANDBY_ON_SLEEP_NONE = "none"; + /** + * @hide + */ + @StringDef({ + SEND_STANDBY_ON_SLEEP_TO_TV, + SEND_STANDBY_ON_SLEEP_BROADCAST, + SEND_STANDBY_ON_SLEEP_NONE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StandbyBehavior { + } + // 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. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java old mode 100755 new mode 100644 index 485828f899a24..bd7e610b6b9ec --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9793,6 +9793,21 @@ public final class Settings { public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED = "hdmi_control_auto_device_off_enabled"; + /** + * Property to decide which devices the playback device can send a message to upon + * going to sleep. Supported values are: + *
    + *
  • {@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_TO_TV} to TV only.
  • + *
  • {@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_BROADCAST} to all devices in the + * network.
  • + *
  • {@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_NONE} no message sent.
  • + *
+ * + * @hide + */ + public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP = + "hdmi_control_send_standby_on_sleep"; + /** * Whether or not media is shown automatically when bypassing as a heads up. * @hide diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 74ab004129679..5d77a2a85d863 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -297,6 +297,7 @@ public class SettingsBackupTest { Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, + Settings.Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 64d70d6601f61..596c1eccfabe6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -85,6 +85,14 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { // The option is false by default. Update settings db as well to have the right // initial setting on UI. mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, mAutoTvOff); + + // Initialize settings database with System Property value. This will be the initial + // setting on the UI. If no System Property is set, the option is set to to_tv by default. + mService.writeStringSetting(Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + mService.readStringSetting(Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiProperties.send_standby_on_sleep().orElse( + HdmiProperties.send_standby_on_sleep_values.TO_TV).name() + .toLowerCase())); } @Override @@ -188,8 +196,26 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { } switch (standbyAction) { case HdmiControlService.STANDBY_SCREEN_OFF: - mService.sendCecCommand( - HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); + // Get latest setting value + @HdmiControlManager.StandbyBehavior + String sendStandbyOnSleep = mService.readStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiProperties.send_standby_on_sleep().orElse( + HdmiProperties.send_standby_on_sleep_values.TO_TV).name() + .toLowerCase()); + switch (sendStandbyOnSleep) { + case HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV: + mService.sendCecCommand( + HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); + break; + case HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST: + mService.sendCecCommand( + HdmiCecMessageBuilder.buildStandby(mAddress, + Constants.ADDR_BROADCAST)); + break; + case HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE: + break; + } break; case HdmiControlService.STANDBY_SHUTDOWN: // ACTION_SHUTDOWN is taken as a signal to power off all the devices. diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index a8a9a368cc710..7007f8b1df506 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -772,6 +772,11 @@ public class HdmiControlService extends SystemService { return content; } + void writeStringSetting(String key, String value) { + ContentResolver cr = getContext().getContentResolver(); + Global.putString(cr, key, value); + } + private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 35f9ca5cce681..32054632dafc2 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -15,6 +15,7 @@ */ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -22,6 +23,7 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.hardware.hdmi.HdmiControlManager; import android.os.Handler; import android.os.IPowerManager; import android.os.IThermalService; @@ -29,6 +31,7 @@ import android.os.Looper; import android.os.PowerManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; +import android.provider.Settings.Global; import android.sysprop.HdmiProperties; import android.view.KeyEvent; @@ -324,29 +327,117 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test - public void handleOnStandby_ScreenOff_NotActiveSource() { + public void handleOnStandby_ScreenOff_NotActiveSource_ToTv() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); mHdmiCecLocalDevicePlayback.setIsActiveSource(false); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); mTestLooper.dispatchAll(); - HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby( + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); - assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessage); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast); } @Test - public void handleOnStandby_ScreenOff_ActiveSource() { + public void handleOnStandby_ScreenOff_NotActiveSource_Broadcast() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + mHdmiCecLocalDevicePlayback.setIsActiveSource(false); + mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); + mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast); + } + + @Test + public void handleOnStandby_ScreenOff_NotActiveSource_None() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + mHdmiCecLocalDevicePlayback.setIsActiveSource(false); + mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); + mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast); + } + + @Test + public void handleOnStandby_ScreenOff_ActiveSource_ToTv() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); mHdmiCecLocalDevicePlayback.setIsActiveSource(true); mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); mTestLooper.dispatchAll(); - HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby( + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); - assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessage); + assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast); + } + + @Test + public void handleOnStandby_ScreenOff_ActiveSource_Broadcast() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); + mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast); + } + + @Test + public void handleOnStandby_ScreenOff_ActiveSource_None() { + mHdmiCecLocalDevicePlayback.mService.writeStringSetting( + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE); + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true); + mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV); + HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby( + mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast); } @Test