Merge "Add setting to enable the eARC feature"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fe66952..2e0c76a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4160,6 +4160,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.String> getAllowedCecSettingStringValues(@NonNull String);
     method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
     method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
+    method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getEarcEnabled();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecEnabled();
     method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVersion();
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getHdmiCecVolumeControlEnabled();
@@ -4182,6 +4183,7 @@
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHdmiCecEnabledChangeListener(@NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
     method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setEarcEnabled(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecEnabled(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVersion(@NonNull int);
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
@@ -4236,6 +4238,8 @@
     field public static final int DEVICE_EVENT_ADD_DEVICE = 1; // 0x1
     field public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; // 0x2
     field public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; // 0x3
+    field public static final int EARC_FEATURE_DISABLED = 0; // 0x0
+    field public static final int EARC_FEATURE_ENABLED = 1; // 0x1
     field public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
     field public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
     field public static final int HDMI_CEC_CONTROL_DISABLED = 0; // 0x0
@@ -4295,6 +4299,7 @@
     field public static final int RESULT_TIMEOUT = 1; // 0x1
     field public static final int ROUTING_CONTROL_DISABLED = 0; // 0x0
     field public static final int ROUTING_CONTROL_ENABLED = 1; // 0x1
+    field public static final String SETTING_NAME_EARC_ENABLED = "earc_enabled";
     field public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0; // 0x0
     field public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1; // 0x1
     field public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; // 0x0
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index cca8688..96773f8 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -50,7 +50,7 @@
 
 /**
  * The {@link HdmiControlManager} class is used to send HDMI control messages
- * to attached CEC devices.
+ * to attached CEC devices. It also allows to control the eARC feature.
  *
  * <p>Provides various HDMI client instances that represent HDMI-CEC logical devices
  * hosted in the system. {@link #getTvClient()}, for instance will return an
@@ -775,6 +775,31 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SadPresenceInQuery {}
 
+    // -- Whether eARC is enabled or disabled.
+    /**
+     * eARC enabled.
+     *
+     * @see HdmiControlManager#SETTING_NAME_EARC_ENABLED
+     */
+    public static final int EARC_FEATURE_ENABLED = 1;
+    /**
+     * eARC disabled.
+     *
+     * @see HdmiControlManager#SETTING_NAME_EARC_ENABLED
+     */
+    public static final int EARC_FEATURE_DISABLED = 0;
+    /**
+     * @hide
+     *
+     * @see HdmiControlManager#SETTING_NAME_EARC_ENABLED
+     */
+    @IntDef(prefix = { "EARC_FEATURE" }, value = {
+            EARC_FEATURE_ENABLED,
+            EARC_FEATURE_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EarcFeature {}
+
     // -- Settings available in the CEC Configuration.
     /**
      * Name of a setting deciding whether the CEC is enabled.
@@ -1032,9 +1057,17 @@
      */
     public static final String CEC_SETTING_NAME_QUERY_SAD_MAX = "query_sad_max";
     /**
+     * Name of a setting representing whether eARC is enabled or not.
+     *
+     * @see HdmiControlManager#setEarcEnabled(int)
+     */
+    public static final String SETTING_NAME_EARC_ENABLED = "earc_enabled";
+    /**
      * @hide
      */
-    @StringDef(prefix = { "CEC_SETTING_NAME_" }, value = {
+    // TODO(b/240379115): change names of CEC settings so that their prefix matches with the other
+    // HDMI control settings.
+    @StringDef(value = {
         CEC_SETTING_NAME_HDMI_CEC_ENABLED,
         CEC_SETTING_NAME_HDMI_CEC_VERSION,
         CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -1066,8 +1099,9 @@
         CEC_SETTING_NAME_QUERY_SAD_DST,
         CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
         CEC_SETTING_NAME_QUERY_SAD_MAX,
+        SETTING_NAME_EARC_ENABLED,
     })
-    public @interface CecSettingName {}
+    public @interface SettingName {}
 
     /**
      * @hide
@@ -1701,7 +1735,7 @@
     public void addHotplugEventListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull HotplugEventListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "addHotplugEventListener: HdmiControlService is not available");
             return;
         }
         if (mHotplugEventListeners.containsKey(listener)) {
@@ -1726,7 +1760,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void removeHotplugEventListener(HotplugEventListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "removeHotplugEventListener: HdmiControlService is not available");
             return;
         }
         IHdmiHotplugEventListener wrappedListener = mHotplugEventListeners.remove(listener);
@@ -1794,7 +1828,7 @@
     public void addHdmiControlStatusChangeListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull HdmiControlStatusChangeListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "addHdmiControlStatusChangeListener: HdmiControlService is not available");
             return;
         }
         if (mHdmiControlStatusChangeListeners.containsKey(listener)) {
@@ -1821,7 +1855,8 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG,
+                    "removeHdmiControlStatusChangeListener: HdmiControlService is not available");
             return;
         }
         IHdmiControlStatusChangeListener wrappedListener =
@@ -1870,7 +1905,8 @@
     public void addHdmiCecVolumeControlFeatureListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull HdmiCecVolumeControlFeatureListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG,
+                    "addHdmiCecVolumeControlFeatureListener: HdmiControlService is not available");
             return;
         }
         if (mHdmiCecVolumeControlFeatureListeners.containsKey(listener)) {
@@ -1898,7 +1934,9 @@
     public void removeHdmiCecVolumeControlFeatureListener(
             HdmiCecVolumeControlFeatureListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG,
+                    "removeHdmiCecVolumeControlFeatureListener: HdmiControlService is not "
+                            + "available");
             return;
         }
         IHdmiCecVolumeControlFeatureListener wrappedListener =
@@ -1938,7 +1976,7 @@
          *
          * @param setting name of a CEC setting that changed
          */
-        void onChange(@NonNull @CecSettingName String setting);
+        void onChange(@NonNull @SettingName String setting);
     }
 
     private final ArrayMap<String,
@@ -1946,11 +1984,11 @@
                     mCecSettingChangeListeners = new ArrayMap<>();
 
     private void addCecSettingChangeListener(
-            @NonNull @CecSettingName String setting,
+            @NonNull @SettingName String setting,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull CecSettingChangeListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "addCecSettingChangeListener: HdmiControlService is not available");
             return;
         }
         if (mCecSettingChangeListeners.containsKey(setting)
@@ -1972,10 +2010,10 @@
     }
 
     private void removeCecSettingChangeListener(
-            @NonNull @CecSettingName String setting,
+            @NonNull @SettingName String setting,
             @NonNull CecSettingChangeListener listener) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "removeCecSettingChangeListener: HdmiControlService is not available");
             return;
         }
         IHdmiCecSettingChangeListener wrappedListener =
@@ -2008,17 +2046,20 @@
     }
 
     /**
-     * Get a set of user-modifiable settings.
+     * Get a set of user-modifiable HDMI control settings.
+     * This applies to CEC settings and eARC settings.
      *
      * @return a set of user-modifiable settings.
      * @throws RuntimeException when the HdmiControlService is not available.
      */
+    // TODO(b/240379115): rename this API to represent that this applies to all HDMI control
+    // settings and not just CEC settings.
     @NonNull
-    @CecSettingName
+    @SettingName
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public List<String> getUserCecSettings() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getUserCecSettings: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2029,7 +2070,9 @@
     }
 
     /**
-     * Get a set of allowed values for a setting (string value-type).
+     * Get a set of allowed values for an HDMI control setting (string value-type).
+     * This applies to CEC settings and eARC settings.
+     *
      *
      * @param name name of the setting
      * @return a set of allowed values for a settings. {@code null} on failure.
@@ -2037,11 +2080,13 @@
      * @throws IllegalArgumentException when setting {@code name} value type is invalid.
      * @throws RuntimeException when the HdmiControlService is not available.
      */
+    // TODO(b/240379115): rename this API to represent that this applies to all HDMI control
+    // settings and not just CEC settings.
     @NonNull
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
-    public List<String> getAllowedCecSettingStringValues(@NonNull @CecSettingName String name) {
+    public List<String> getAllowedCecSettingStringValues(@NonNull @SettingName String name) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getAllowedCecSettingStringValues: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2052,7 +2097,8 @@
     }
 
     /**
-     * Get a set of allowed values for a setting (int value-type).
+     * Get a set of allowed values for an HDMI control setting (int value-type).
+     * This applies to CEC settings and eARC settings.
      *
      * @param name name of the setting
      * @return a set of allowed values for a settings. {@code null} on failure.
@@ -2060,11 +2106,13 @@
      * @throws IllegalArgumentException when setting {@code name} value type is invalid.
      * @throws RuntimeException when the HdmiControlService is not available.
      */
+    // TODO(b/240379115): rename this API to represent that this applies to all HDMI control
+    // settings and not just CEC settings.
     @NonNull
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
-    public List<Integer> getAllowedCecSettingIntValues(@NonNull @CecSettingName String name) {
+    public List<Integer> getAllowedCecSettingIntValues(@NonNull @SettingName String name) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getAllowedCecSettingIntValues: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2083,7 +2131,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecEnabled(@NonNull @HdmiCecControl int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setHdmiCecEnabled: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2103,7 +2151,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getHdmiCecEnabled() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getHdmiCecEnabled: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2161,7 +2209,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setHdmiCecVersion(@NonNull @HdmiCecVersion int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setHdmiCecVersion: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2183,7 +2231,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getHdmiCecVersion() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getHdmiCecVersion: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2206,7 +2254,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setRoutingControl(@NonNull @RoutingControl int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setRoutingControl: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2231,7 +2279,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getRoutingControl() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getRoutingControl: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2252,7 +2300,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setPowerControlMode(@NonNull @PowerControlMode String value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setPowerControlMode: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2275,7 +2323,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public String getPowerControlMode() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getPowerControlMode: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2296,7 +2344,8 @@
     public void setPowerStateChangeOnActiveSourceLost(
             @NonNull @ActiveSourceLostBehavior String value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG,
+                    "setPowerStateChangeOnActiveSourceLost: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2319,7 +2368,8 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public String getPowerStateChangeOnActiveSourceLost() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG,
+                    "getPowerStateChangeOnActiveSourceLost: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2344,7 +2394,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setSystemAudioControl: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2370,7 +2420,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getSystemAudioControl() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getSystemAudioControl: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2390,7 +2440,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setSystemAudioModeMuting: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2412,7 +2462,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getSystemAudioModeMuting() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getSystemAudioModeMuting: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2433,7 +2483,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvWakeOnOneTouchPlay(@NonNull @TvWakeOnOneTouchPlay int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setTvWakeOnOneTouchPlay: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2456,7 +2506,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getTvWakeOnOneTouchPlay() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getTvWakeOnOneTouchPlay: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2477,7 +2527,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void setTvSendStandbyOnSleep(@NonNull @TvSendStandbyOnSleep int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setTvSendStandbyOnSleep: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2500,7 +2550,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getTvSendStandbyOnSleep() {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getTvSendStandbyOnSleep: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2532,7 +2582,7 @@
     public void setSadPresenceInQuery(@NonNull @CecSettingSad String setting,
             @SadPresenceInQuery int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setSadPresenceInQuery: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2566,7 +2616,7 @@
     public void setSadsPresenceInQuery(@NonNull @CecSettingSad List<String> settings,
             @SadPresenceInQuery int value) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "setSadsPresenceInQuery: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2598,7 +2648,7 @@
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public int getSadPresenceInQuery(@NonNull @CecSettingSad String setting) {
         if (mService == null) {
-            Log.e(TAG, "HdmiControlService is not available");
+            Log.e(TAG, "getSadPresenceInQuery: HdmiControlService is not available");
             throw new RuntimeException("HdmiControlService is not available");
         }
         try {
@@ -2607,4 +2657,44 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Set the global status of eARC.
+     *
+     * <p>This allows to enable/disable the eARC feature on the device. If the feature is enabled
+     * and the hardware supports eARC as well, the device can attempt to establish an eARC
+     * connection.
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setEarcEnabled(@NonNull @EarcFeature int value) {
+        if (mService == null) {
+            Log.e(TAG, "setEarcEnabled: HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingIntValue(SETTING_NAME_EARC_ENABLED, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the current global status of eARC.
+     *
+     * <p>Reflects whether the eARC feature is currently enabled on the device.
+     */
+    @NonNull
+    @EarcFeature
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public int getEarcEnabled() {
+        if (mService == null) {
+            Log.e(TAG, "getEarcEnabled: HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingIntValue(SETTING_NAME_EARC_ENABLED);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b92d96f..86b0715 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5539,6 +5539,13 @@
     <bool name="config_cecQuerySadMaxDisabled_allowed">true</bool>
     <bool name="config_cecQuerySadMaxDisabled_default">false</bool>
 
+    <!-- eARC Configuration -->
+    <bool name="config_earcEnabled_userConfigurable">true</bool>
+    <bool name="config_earcFeatureEnabled_allowed">true</bool>
+    <bool name="config_earcFeatureEnabled_default">true</bool>
+    <bool name="config_earcFeatureDisabled_allowed">true</bool>
+    <bool name="config_earcFeatureDisabled_default">false</bool>
+
     <!-- Whether app hibernation deletes OAT artifact files as part of global hibernation. -->
     <bool name="config_hibernationDeletesOatArtifactsEnabled">true</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9d7e6f8..98529bc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4740,6 +4740,13 @@
   <java-symbol type="bool" name="config_cecQuerySadMaxDisabled_allowed" />
   <java-symbol type="bool" name="config_cecQuerySadMaxDisabled_default" />
 
+  <!-- eARC Configuration -->
+  <java-symbol type="bool" name="config_earcEnabled_userConfigurable" />
+  <java-symbol type="bool" name="config_earcFeatureEnabled_allowed" />
+  <java-symbol type="bool" name="config_earcFeatureEnabled_default" />
+  <java-symbol type="bool" name="config_earcFeatureDisabled_allowed" />
+  <java-symbol type="bool" name="config_earcFeatureDisabled_default" />
+
   <!-- Ids for RemoteViews -->
   <java-symbol type="id" name="remote_views_next_child" />
   <java-symbol type="id" name="remote_views_stable_id" />
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 79820a2..827aafa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -16,7 +16,7 @@
 
 package com.android.server.hdmi;
 
-import static android.hardware.hdmi.HdmiControlManager.CecSettingName;
+import static android.hardware.hdmi.HdmiControlManager.SettingName;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -45,7 +45,10 @@
 
 /**
  * The {@link HdmiCecConfig} class is used for getting information about
- * available HDMI CEC settings.
+ * available HDMI control settings, including CEC settings and eARC settings.
+ *
+ * TODO(b/240379115): rename this class and related methods in this package to represent that the
+ * settings storage mechanism applies to all HDMI control settings and not just CEC settings.
  */
 public class HdmiCecConfig {
     private static final String TAG = "HdmiCecConfig";
@@ -105,7 +108,7 @@
          *
          * @param setting name of a CEC setting that changed
          */
-        void onChange(@NonNull @CecSettingName String setting);
+        void onChange(@NonNull @SettingName String setting);
     }
 
     /**
@@ -204,21 +207,21 @@
 
     protected class Setting {
         @NonNull private final Context mContext;
-        @NonNull private final @CecSettingName String mName;
+        @NonNull private final @SettingName String mName;
         private final boolean mUserConfigurable;
 
         private Value mDefaultValue = null;
         private List<Value> mAllowedValues = new ArrayList<>();
 
         Setting(@NonNull Context context,
-                @NonNull @CecSettingName String name,
+                @NonNull @SettingName String name,
                 int userConfResId) {
             mContext = context;
             mName = name;
             mUserConfigurable = mContext.getResources().getBoolean(userConfResId);
         }
 
-        public @CecSettingName String getName() {
+        public @SettingName String getName() {
             return mName;
         }
 
@@ -663,6 +666,16 @@
                 R.bool.config_cecQuerySadMaxDisabled_allowed,
                 R.bool.config_cecQuerySadMaxDisabled_default);
 
+        Setting earcEnabled = registerSetting(
+                HdmiControlManager.SETTING_NAME_EARC_ENABLED,
+                R.bool.config_earcEnabled_userConfigurable);
+        earcEnabled.registerValue(HdmiControlManager.EARC_FEATURE_ENABLED,
+                R.bool.config_earcFeatureEnabled_allowed,
+                R.bool.config_earcFeatureEnabled_default);
+        earcEnabled.registerValue(HdmiControlManager.EARC_FEATURE_DISABLED,
+                R.bool.config_earcFeatureDisabled_allowed,
+                R.bool.config_earcFeatureDisabled_default);
+
         verifySettings();
     }
 
@@ -670,7 +683,7 @@
         this(context, new StorageAdapter(context));
     }
 
-    private Setting registerSetting(@NonNull @CecSettingName String name,
+    private Setting registerSetting(@NonNull @SettingName String name,
                                int userConfResId) {
         Setting setting = new Setting(mContext, name, userConfResId);
         mSettings.put(name, setting);
@@ -760,6 +773,8 @@
                 return STORAGE_SHARED_PREFS;
             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
                 return STORAGE_SHARED_PREFS;
+            case HdmiControlManager.SETTING_NAME_EARC_ENABLED:
+                return STORAGE_SHARED_PREFS;
             default:
                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
                         + "' storage.");
@@ -833,6 +848,8 @@
                 return setting.getName();
             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
                 return setting.getName();
+            case HdmiControlManager.SETTING_NAME_EARC_ENABLED:
+                return setting.getName();
             default:
                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
                     + "' storage key.");
@@ -871,14 +888,6 @@
         }
     }
 
-    private void notifySettingChanged(@NonNull @CecSettingName String name) {
-        Setting setting = getSetting(name);
-        if (setting == null) {
-            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
-        }
-        notifySettingChanged(setting);
-    }
-
     protected void notifySettingChanged(@NonNull Setting setting) {
         synchronized (mLock) {
             ArrayMap<SettingChangeListener, Executor> listeners =
@@ -902,7 +911,7 @@
     /**
      * Register change listener for a given setting name using DirectExecutor.
      */
-    public void registerChangeListener(@NonNull @CecSettingName String name,
+    public void registerChangeListener(@NonNull @SettingName String name,
                                        SettingChangeListener listener) {
         registerChangeListener(name, listener, ConcurrentUtils.DIRECT_EXECUTOR);
     }
@@ -910,7 +919,7 @@
     /**
      * Register change listener for a given setting name and executor.
      */
-    public void registerChangeListener(@NonNull @CecSettingName String name,
+    public void registerChangeListener(@NonNull @SettingName String name,
                                        SettingChangeListener listener,
                                        Executor executor) {
         Setting setting = getSetting(name);
@@ -933,7 +942,7 @@
     /**
      * Remove change listener for a given setting name.
      */
-    public void removeChangeListener(@NonNull @CecSettingName String name,
+    public void removeChangeListener(@NonNull @SettingName String name,
                                      SettingChangeListener listener) {
         Setting setting = getSetting(name);
         if (setting == null) {
@@ -954,14 +963,14 @@
     /**
      * Returns a list of all settings based on the XML metadata.
      */
-    public @CecSettingName List<String> getAllSettings() {
+    public @SettingName List<String> getAllSettings() {
         return new ArrayList<>(mSettings.keySet());
     }
 
     /**
      * Returns a list of user-modifiable settings based on the XML metadata.
      */
-    public @CecSettingName List<String> getUserSettings() {
+    public @SettingName List<String> getUserSettings() {
         List<String> settings = new ArrayList<>();
         for (Setting setting: mSettings.values()) {
             if (setting.getUserConfigurable()) {
@@ -975,7 +984,7 @@
      * For a given setting name returns true if and only if the value type of that
      * setting is a string.
      */
-    public boolean isStringValueType(@NonNull @CecSettingName String name) {
+    public boolean isStringValueType(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -987,7 +996,7 @@
      * For a given setting name returns true if and only if the value type of that
      * setting is an int.
      */
-    public boolean isIntValueType(@NonNull @CecSettingName String name) {
+    public boolean isIntValueType(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -998,7 +1007,7 @@
     /**
      * For a given setting name returns values that are allowed for that setting (string).
      */
-    public List<String> getAllowedStringValues(@NonNull @CecSettingName String name) {
+    public List<String> getAllowedStringValues(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1017,7 +1026,7 @@
     /**
      * For a given setting name returns values that are allowed for that setting (string).
      */
-    public List<Integer> getAllowedIntValues(@NonNull @CecSettingName String name) {
+    public List<Integer> getAllowedIntValues(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1036,7 +1045,7 @@
     /**
      * For a given setting name returns the default value for that setting (string).
      */
-    public String getDefaultStringValue(@NonNull @CecSettingName String name) {
+    public String getDefaultStringValue(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1051,7 +1060,7 @@
     /**
      * For a given setting name returns the default value for that setting (int).
      */
-    public int getDefaultIntValue(@NonNull @CecSettingName String name) {
+    public int getDefaultIntValue(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1066,7 +1075,7 @@
     /**
      * For a given setting name returns the current value of that setting (string).
      */
-    public String getStringValue(@NonNull @CecSettingName String name) {
+    public String getStringValue(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1082,7 +1091,7 @@
     /**
      * For a given setting name returns the current value of that setting (int).
      */
-    public int getIntValue(@NonNull @CecSettingName String name) {
+    public int getIntValue(@NonNull @SettingName String name) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1100,7 +1109,7 @@
     /**
      * For a given setting name and value sets the current value of that setting (string).
      */
-    public void setStringValue(@NonNull @CecSettingName String name, @NonNull String value) {
+    public void setStringValue(@NonNull @SettingName String name, @NonNull String value) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
@@ -1123,7 +1132,7 @@
     /**
      * For a given setting name and value sets the current value of that setting (int).
      */
-    public void setIntValue(@NonNull @CecSettingName String name, int value) {
+    public void setIntValue(@NonNull @SettingName String name, int value) {
         Setting setting = getSetting(name);
         if (setting == null) {
             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 0028969..5722ff3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -348,6 +348,17 @@
         doReturn(true).when(resources).getBoolean(R.bool.config_cecQuerySadMaxDisabled_allowed);
         doReturn(false).when(resources).getBoolean(R.bool.config_cecQuerySadMaxDisabled_default);
 
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_earcEnabled_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_earcFeatureEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_earcFeatureEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_earcFeatureDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_earcFeatureDisabled_default);
+
         return resources;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 8e756ae..392d7f1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -105,7 +105,8 @@
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
-                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX,
+                    HdmiControlManager.SETTING_NAME_EARC_ENABLED);
     }
 
     @Test
@@ -144,7 +145,8 @@
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
-                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX,
+                    HdmiControlManager.SETTING_NAME_EARC_ENABLED);
     }
 
     @Test
@@ -183,7 +185,8 @@
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
                     HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
-                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX);
+                    HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX,
+                    HdmiControlManager.SETTING_NAME_EARC_ENABLED);
     }
 
     @Test