[CEC Configuration] Add CEC Configuration APIs

Bug: 166426337
Bug: 169144568
Test: atest HdmiCecConfigTest

Change-Id: I7f109027103e73955d09b251a7a94aee9beded67
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 5b186c7..9471e49 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -319,6 +319,29 @@
     /** The HdmiControlService will be disabled to standby. */
     public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3;
 
+    // -- Whether the HDMI CEC is enabled or disabled.
+    /**
+     * HDMI CEC enabled.
+     *
+     * @hide
+     */
+    public static final String HDMI_CEC_CONTROL_ENABLED = "1";
+    /**
+     * HDMI CEC disabled.
+     *
+     * @hide
+     */
+    public static final String HDMI_CEC_CONTROL_DISABLED = "0";
+    /**
+     * @hide
+     */
+    @StringDef({
+            HDMI_CEC_CONTROL_ENABLED,
+            HDMI_CEC_CONTROL_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HdmiCecControl {}
+
     // -- Which devices the playback device can send a <Standby> message to upon going to sleep.
     /**
      * Send <Standby> to TV only.
@@ -347,8 +370,90 @@
             SEND_STANDBY_ON_SLEEP_NONE
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface StandbyBehavior {
-    }
+    public @interface StandbyBehavior {}
+
+    // -- Which power state action should be taken when Active Source is lost.
+    /**
+     * No action to be taken.
+     *
+     * @hide
+     */
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
+    /**
+     * Go to standby immediately.
+     *
+     * @hide
+     */
+    public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
+    /**
+     * @hide
+     */
+    @StringDef({
+            POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+            POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ActiveSourceLostBehavior {}
+
+    // -- Whether System Audio Mode muting is enabled or disabled.
+    /**
+     * System Audio Mode muting enabled.
+     *
+     * @hide
+     */
+    public static final String SYSTEM_AUDIO_MODE_MUTING_ENABLED = "1";
+    /**
+     * System Audio Mode muting disabled.
+     *
+     * @hide
+     */
+    public static final String SYSTEM_AUDIO_MODE_MUTING_DISABLED = "0";
+    /**
+     * @hide
+     */
+    @StringDef({
+            SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+            SYSTEM_AUDIO_MODE_MUTING_DISABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemAudioModeMuting {}
+
+    // -- Settings available in the CEC Configuration.
+    /**
+     * Name of a setting deciding whether the CEC is enabled.
+     *
+     * @hide
+     */
+    public static final String SETTING_NAME_HDMI_CEC_ENABLED = "hdmi_cec_enabled";
+    /**
+     * Name of a setting deciding on the Standby message behaviour on sleep.
+     *
+     * @hide
+     */
+    public static final String SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep";
+    /**
+     * Name of a setting deciding on power state action when losing Active Source.
+     *
+     * @hide
+     */
+    public static final String SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+            "power_state_change_on_active_source_lost";
+    /**
+     * Name of a setting deciding whether System Audio Muting is allowed.
+     *
+     * @hide
+     */
+    public static final String SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
+    /**
+     * @hide
+     */
+    @StringDef({
+        SETTING_NAME_HDMI_CEC_ENABLED,
+        SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+        SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+        SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+    })
+    public @interface SettingName {}
 
     // True if we have a logical device of type playback hosted in the system.
     private final boolean mHasPlaybackDevice;
@@ -1186,4 +1291,233 @@
             }
         };
     }
+
+    /**
+     * Get a set of user-modifiable settings.
+     *
+     * @return a set of user-modifiable settings.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public List<String> getAvailableCecSettings() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getAvailableCecSettings();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get a set of allowed values for a settings.
+     *
+     * @param name name of the setting
+     * @return a set of allowed values for a settings. {@code null} on failure.
+     * @throws IllegalArgumentException when setting {@code name} does not exist.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public List<String> getAllowedCecSettingValues(@NonNull String name) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getAllowedCecSettingValues(name);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'hdmi_cec_enabled' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setHdmiCecEnabled(@NonNull @HdmiCecControl String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(SETTING_NAME_HDMI_CEC_ENABLED, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'hdmi_cec_enabled' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @HdmiCecControl
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getHdmiCecEnabled() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(SETTING_NAME_HDMI_CEC_ENABLED);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'send_standby_on_sleep' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'send_standby_on_sleep' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @StandbyBehavior
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getSendStandbyOnSleep() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'power_state_change_on_active_source_lost' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setPowerStateChangeOnActiveSourceLost(
+            @NonNull @ActiveSourceLostBehavior String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(
+                    SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'power_state_change_on_active_source_lost' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @ActiveSourceLostBehavior
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getPowerStateChangeOnActiveSourceLost() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(
+                    SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the 'system_audio_mode_muting' option.
+     *
+     * @param value the desired value
+     * @throws IllegalArgumentException when the new value is not allowed.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void setSystemAudioModeMuting(@NonNull @SystemAudioModeMuting String value) {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            mService.setCecSettingValue(SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, value);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the value of 'system_audio_mode_muting' option.
+     *
+     * @return the current value.
+     * @throws RuntimeException when the HdmiControlService is not available.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemAudioModeMuting
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public String getSystemAudioModeMuting() {
+        if (mService == null) {
+            Log.e(TAG, "HdmiControlService is not available");
+            throw new RuntimeException("HdmiControlService is not available");
+        }
+        try {
+            return mService.getCecSettingValue(SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 0289635..46a3c4f 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -293,6 +294,26 @@
                 IHdmiCecVolumeControlFeatureListener listener) {
             HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
         }
+
+        @Override
+        public List<String> getAvailableCecSettings() {
+            return HdmiControlServiceWrapper.this.getAvailableCecSettings();
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            return HdmiControlServiceWrapper.this.getAllowedCecSettingValues(name);
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            return HdmiControlServiceWrapper.this.getCecSettingValue(name);
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+            HdmiControlServiceWrapper.this.setCecSettingValue(name, value);
+        }
     };
 
     @BinderThread
@@ -466,4 +487,22 @@
     /** @hide */
     public void removeHdmiCecVolumeControlFeatureListener(
             IHdmiCecVolumeControlFeatureListener listener) {}
+
+    /** @hide */
+    public List<String> getAvailableCecSettings() {
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    public List<String> getAllowedCecSettingValues(String name) {
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    public String getCecSettingValue(String name) {
+        return "";
+    }
+
+    /** @hide */
+    public void setCecSettingValue(String name, String value) {}
 }
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 4c724ef..3478b23 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -87,4 +87,8 @@
     boolean isHdmiCecVolumeControlEnabled();
     void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
     void setSystemAudioModeOnForAudioOnlySource();
+    List<String> getAvailableCecSettings();
+    List<String> getAllowedCecSettingValues(String name);
+    String getCecSettingValue(String name);
+    void setCecSettingValue(String name, String value);
 }
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index a4f2065..f9140bc 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -28,6 +28,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -373,6 +374,25 @@
         public void removeHdmiCecVolumeControlFeatureListener(
                 IHdmiCecVolumeControlFeatureListener listener) {
         }
+
+        @Override
+        public List<String> getAvailableCecSettings() {
+            return new ArrayList<>();
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            return new ArrayList<>();
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            return "";
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+        }
     }
 
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
new file mode 100644
index 0000000..393a66e
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Environment;
+import android.os.SystemProperties;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.Setting;
+import com.android.server.hdmi.cec.config.Value;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * The {@link HdmiCecConfig} class is used for getting information about
+ * available HDMI CEC settings.
+ */
+public class HdmiCecConfig {
+    private static final String TAG = "HdmiCecConfig";
+
+    private static final String ETC_DIR = "etc";
+    private static final String CONFIG_FILE = "cec_config.xml";
+
+    @Nullable private CecSettings mProductConfig = null;
+    @Nullable private CecSettings mVendorOverride = null;
+    @Nullable private StorageAdapter mStorageAdapter = null;
+
+    @IntDef({
+        STORAGE_SYSPROPS,
+        STORAGE_GLOBAL_SETTINGS,
+    })
+    private @interface Storage {}
+
+    private static final int STORAGE_SYSPROPS = 0;
+    private static final int STORAGE_GLOBAL_SETTINGS = 1;
+
+    /**
+     * System property key for Power State Change on Active Source Lost.
+     */
+    public static final String SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
+            "ro.hdmi.cec.source.power_state_change_on_active_source_lost";
+
+    /**
+     * System property key for Audio Mode Muting.
+     */
+    public static final String SYSPROP_SYSTEM_AUDIO_MODE_MUTING =
+            "ro.hdmi.cec.audio.system_audio_mode_muting.enabled";
+
+    /**
+     * Setting storage input/output helper class.
+     */
+    public static class StorageAdapter {
+        /**
+         * Read the value from a system property.
+         * Returns the given default value if the system property is not set.
+         */
+        public String retrieveSystemProperty(@NonNull String storageKey,
+                                             @NonNull String defaultValue) {
+            return SystemProperties.get(storageKey, defaultValue);
+        }
+
+        /**
+         * Write the value to a system property.
+         */
+        public void storeSystemProperty(@NonNull String storageKey,
+                                        @NonNull String value) {
+            SystemProperties.set(storageKey, value);
+        }
+
+        /**
+         * Read the value from a global setting.
+         * Returns the given default value if the system property is not set.
+         */
+        public String retrieveGlobalSetting(@NonNull Context context,
+                                            @NonNull String storageKey,
+                                            @NonNull String defaultValue) {
+            String value = Global.getString(context.getContentResolver(), storageKey);
+            return value != null ? value : defaultValue;
+        }
+
+        /**
+         * Write the value to a global setting.
+         */
+        public void storeGlobalSetting(@NonNull Context context,
+                                       @NonNull String storageKey,
+                                       @NonNull String value) {
+            Global.putString(context.getContentResolver(), storageKey, value);
+        }
+    }
+
+    @VisibleForTesting
+    HdmiCecConfig(@Nullable CecSettings productConfig,
+                  @Nullable CecSettings vendorOverride,
+                  @Nullable StorageAdapter storageAdapter) {
+        mProductConfig = productConfig;
+        mVendorOverride = vendorOverride;
+        mStorageAdapter = storageAdapter;
+    }
+
+    HdmiCecConfig() {
+        this(readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
+                                                        ETC_DIR, CONFIG_FILE)),
+             readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
+                                                        ETC_DIR, CONFIG_FILE)),
+             new StorageAdapter());
+    }
+
+    @Nullable
+    private static CecSettings readSettingsFromFile(@NonNull File file) {
+        if (!file.exists()) {
+            return null;
+        }
+        if (!file.isFile()) {
+            Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
+            return null;
+        }
+        try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
+            return XmlParser.read(in);
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
+        }
+        return null;
+    }
+
+    @Nullable
+    private Setting getSetting(@NonNull String name) {
+        if (mVendorOverride != null) {
+            // First read from the vendor override.
+            for (Setting setting : mVendorOverride.getSetting()) {
+                if (setting.getName().equals(name)) {
+                    return setting;
+                }
+            }
+        }
+        // If not found, try the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
+            if (setting.getName().equals(name)) {
+                return setting;
+            }
+        }
+        return null;
+    }
+
+    @Storage
+    private int getStorage(@NonNull Setting setting) {
+        switch (setting.getName()) {
+            case HdmiControlManager.SETTING_NAME_HDMI_CEC_ENABLED:
+                return STORAGE_GLOBAL_SETTINGS;
+            case HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+                return STORAGE_GLOBAL_SETTINGS;
+            case HdmiControlManager.SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+                return STORAGE_SYSPROPS;
+            case HdmiControlManager.SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+                return STORAGE_SYSPROPS;
+            default:
+                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                    + "' storage.");
+        }
+    }
+
+    private String getStorageKey(@NonNull Setting setting) {
+        switch (setting.getName()) {
+            case HdmiControlManager.SETTING_NAME_HDMI_CEC_ENABLED:
+                return Global.HDMI_CONTROL_ENABLED;
+            case HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+                return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
+            case HdmiControlManager.SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
+                return SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST;
+            case HdmiControlManager.SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
+                return SYSPROP_SYSTEM_AUDIO_MODE_MUTING;
+            default:
+                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                    + "' storage key.");
+        }
+    }
+
+    private String retrieveValue(@NonNull Context context, @NonNull Setting setting) {
+        @Storage int storage = getStorage(setting);
+        String storageKey = getStorageKey(setting);
+        if (storage == STORAGE_SYSPROPS) {
+            Slog.d(TAG, "Reading '" + storageKey + "' sysprop.");
+            return mStorageAdapter.retrieveSystemProperty(storageKey,
+                    setting.getDefaultValue().getStringValue());
+        } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+            Slog.d(TAG, "Reading '" + storageKey + "' global setting.");
+            return mStorageAdapter.retrieveGlobalSetting(context, storageKey,
+                    setting.getDefaultValue().getStringValue());
+        }
+        return null;
+    }
+
+    private void storeValue(@NonNull Context context, @NonNull Setting setting,
+                            @NonNull String value) {
+        @Storage int storage = getStorage(setting);
+        String storageKey = getStorageKey(setting);
+        if (storage == STORAGE_SYSPROPS) {
+            Slog.d(TAG, "Setting '" + storageKey + "' sysprop.");
+            mStorageAdapter.storeSystemProperty(storageKey, value);
+        } else if (storage == STORAGE_GLOBAL_SETTINGS) {
+            Slog.d(TAG, "Setting '" + storageKey + "' global setting.");
+            mStorageAdapter.storeGlobalSetting(context, storageKey, value);
+        }
+    }
+
+    /**
+     * Returns a list of currently available settings based on the XML metadata.
+     */
+    public List<String> getAvailableSettings() {
+        Set<String> availableSettings = new HashSet<String>();
+        // First read from the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
+            if (setting.getUserConfigurable()) {
+                availableSettings.add(setting.getName());
+            }
+        }
+        if (mVendorOverride != null) {
+            // Next either add or remove based on the vendor override.
+            for (Setting setting : mVendorOverride.getSetting()) {
+                if (setting.getUserConfigurable()) {
+                    availableSettings.add(setting.getName());
+                } else {
+                    availableSettings.remove(setting.getName());
+                }
+            }
+        }
+        return new ArrayList(availableSettings);
+    }
+
+    /**
+     * For a given setting name returns values that are allowed for that setting.
+     */
+    public List<String> getAllowedValues(@NonNull String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        List<String> allowedValues = new ArrayList<String>();
+        for (Value allowedValue : setting.getAllowedValues().getValue()) {
+            allowedValues.add(allowedValue.getStringValue());
+        }
+        return allowedValues;
+    }
+
+    /**
+     * For a given setting name returns the default value for that setting.
+     */
+    public String getDefaultValue(@NonNull String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        return getSetting(name).getDefaultValue().getStringValue();
+    }
+
+    /**
+     * For a given setting name returns the current value of that setting.
+     */
+    public String getValue(@NonNull Context context, @NonNull String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        Slog.d(TAG, "Getting CEC setting value '" + name + "'.");
+        return retrieveValue(context, setting);
+    }
+
+    /**
+     * For a given setting name and value sets the current value of that setting.
+     */
+    public void setValue(@NonNull Context context, @NonNull String name, @NonNull String value) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        if (!setting.getUserConfigurable()) {
+            throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
+        }
+        if (!getAllowedValues(name).contains(value)) {
+            throw new IllegalArgumentException("Invalid CEC setting '" + name
+                                               + "' value: '" + value + "'.");
+        }
+        Slog.d(TAG, "Updating CEC setting '" + name + "' to '" + value + "'.");
+        storeValue(context, setting, value);
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 27bd056..98830cd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -187,6 +187,9 @@
     @GuardedBy("mLock")
     private boolean mHdmiCecVolumeControlEnabled;
 
+    // Make sure HdmiCecConfig is instantiated and the XMLs are read.
+    private final HdmiCecConfig mHdmiCecConfig = new HdmiCecConfig();
+
     /**
      * Interface to report send result.
      */
@@ -2329,6 +2332,50 @@
                 pw.decreaseIndent();
             }
         }
+
+        @Override
+        public List<String> getAvailableCecSettings() {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getAvailableSettings();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<String> getAllowedCecSettingValues(String name) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getAllowedValues(name);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public String getCecSettingValue(String name) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                return HdmiControlService.this.getHdmiCecConfig().getValue(getContext(), name);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setCecSettingValue(String name, String value) {
+            enforceAccessPermission();
+            long token = Binder.clearCallingIdentity();
+            try {
+                HdmiControlService.this.getHdmiCecConfig().setValue(getContext(), name, value);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     // Get the source address to send out commands to devices connected to the current device
@@ -3389,4 +3436,8 @@
         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                 HdmiControlService.PERMISSION);
     }
+
+    HdmiCecConfig getHdmiCecConfig() {
+        return mHdmiCecConfig;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
new file mode 100644
index 0000000..af2a4be
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.cec.config.CecSettings;
+import com.android.server.hdmi.cec.config.XmlParser;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public final class HdmiCecConfigTest {
+    private static final String TAG = "HdmiCecConfigTest";
+
+    private Context mContext;
+
+    @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    @Test
+    public void getAvailableCecSettings_Empty() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAvailableSettings()).isEmpty();
+    }
+
+    @Test
+    public void getAvailableCecSettings_OnlyMasterXml() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"hdmi_cec_enabled\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"0\" />"
+                + "      <value string-value=\"1\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"1\" />"
+                + "  </setting>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAvailableSettings())
+                .containsExactly(HdmiControlManager.SETTING_NAME_HDMI_CEC_ENABLED,
+                                 HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+    }
+
+    @Test
+    public void getAvailableCecSettings_WithOverride() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"hdmi_cec_enabled\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"0\" />"
+                + "      <value string-value=\"1\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"1\" />"
+                + "  </setting>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>",
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>");
+        assertThat(hdmiCecConfig.getAvailableSettings())
+                .containsExactly(HdmiControlManager.SETTING_NAME_HDMI_CEC_ENABLED);
+    }
+
+    @Test
+    public void getAllowedValues_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getAllowedValues("foo"));
+    }
+
+    @Test
+    public void getAllowedValues_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getAllowedValues(
+                    HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
+                                 HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
+                                 HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+    }
+
+    @Test
+    public void getDefaultValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getDefaultValue("foo"));
+    }
+
+    @Test
+    public void getDefaultValue_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getDefaultValue(
+                    HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+    }
+
+    @Test
+    public void getValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.getValue(mContext, "foo"));
+    }
+
+    @Test
+    public void getValue_GlobalSetting_BasicSanity() {
+        when(mStorageAdapter.retrieveGlobalSetting(mContext,
+                  Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+                  HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
+            .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getValue(mContext,
+                    HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP))
+                .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+    }
+
+    @Test
+    public void getValue_SystemProperty_BasicSanity() {
+        when(mStorageAdapter.retrieveSystemProperty(
+                  HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                  HdmiProperties.power_state_change_on_active_source_lost_values
+                      .NONE.name().toLowerCase()))
+                .thenReturn(HdmiProperties.power_state_change_on_active_source_lost_values
+                       .STANDBY_NOW.name().toLowerCase());
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"power_state_change_on_active_source_lost\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"none\" />"
+                + "      <value string-value=\"standby_now\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"none\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThat(hdmiCecConfig.getValue(mContext,
+                    HdmiControlManager.SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
+                .isEqualTo(HdmiProperties.power_state_change_on_active_source_lost_values
+                        .STANDBY_NOW.name().toLowerCase());
+    }
+
+    @Test
+    public void setValue_InvalidSetting() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext, "foo", "bar"));
+    }
+
+    @Test
+    public void setValue_NotConfigurable() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"false\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext,
+                        HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                        HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
+    }
+
+    @Test
+    public void setValue_InvalidValue() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        assertThrows(IllegalArgumentException.class,
+                () -> hdmiCecConfig.setValue(mContext,
+                        HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                        "bar"));
+    }
+
+    @Test
+    public void setValue_GlobalSetting_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"send_standby_on_sleep\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"to_tv\" />"
+                + "      <value string-value=\"broadcast\" />"
+                + "      <value string-value=\"none\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"to_tv\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        hdmiCecConfig.setValue(mContext,
+                               HdmiControlManager.SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+                               HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+        verify(mStorageAdapter).storeGlobalSetting(mContext,
+                  Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+                  HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+    }
+
+    @Test
+    public void setValue_SystemProperty_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = createHdmiCecConfig(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                + "<cec-settings>"
+                + "  <setting name=\"power_state_change_on_active_source_lost\""
+                + "           user-configurable=\"true\">"
+                + "    <allowed-values>"
+                + "      <value string-value=\"none\" />"
+                + "      <value string-value=\"standby_now\" />"
+                + "    </allowed-values>"
+                + "    <default-value string-value=\"none\" />"
+                + "  </setting>"
+                + "</cec-settings>", null);
+        hdmiCecConfig.setValue(mContext,
+                        HdmiControlManager.SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                        HdmiProperties.power_state_change_on_active_source_lost_values
+                            .STANDBY_NOW.name().toLowerCase());
+        verify(mStorageAdapter).storeSystemProperty(
+                  HdmiCecConfig.SYSPROP_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                  HdmiProperties.power_state_change_on_active_source_lost_values
+                      .STANDBY_NOW.name().toLowerCase());
+    }
+
+    private HdmiCecConfig createHdmiCecConfig(String productConfigXml, String vendorOverrideXml) {
+        CecSettings productConfig = null;
+        CecSettings vendorOverride = null;
+        try {
+            productConfig = XmlParser.read(new ByteArrayInputStream(productConfigXml.getBytes()));
+            if (vendorOverrideXml != null) {
+                vendorOverride = XmlParser.read(
+                        new ByteArrayInputStream(vendorOverrideXml.getBytes()));
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+        }
+        return new HdmiCecConfig(productConfig, vendorOverride, mStorageAdapter);
+    }
+}