1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.hdmi;
18 
19 import static android.hardware.hdmi.HdmiControlManager.SettingName;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.StringDef;
25 import android.content.Context;
26 import android.content.SharedPreferences;
27 import android.hardware.hdmi.HdmiControlManager;
28 import android.os.Environment;
29 import android.os.SystemProperties;
30 import android.provider.Settings.Global;
31 import android.util.ArrayMap;
32 import android.util.Slog;
33 
34 import com.android.internal.R;
35 import com.android.internal.annotations.GuardedBy;
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.util.ConcurrentUtils;
38 
39 import java.io.File;
40 import java.util.ArrayList;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map.Entry;
44 import java.util.concurrent.Executor;
45 
46 /**
47  * The {@link HdmiCecConfig} class is used for getting information about
48  * available HDMI control settings, including CEC settings and eARC settings.
49  *
50  * TODO(b/240379115): rename this class and related methods in this package to represent that the
51  * settings storage mechanism applies to all HDMI control settings and not just CEC settings.
52  */
53 public class HdmiCecConfig {
54     private static final String TAG = "HdmiCecConfig";
55 
56     private static final String ETC_DIR = "etc";
57     private static final String CONFIG_FILE = "cec_config.xml";
58     private static final String SHARED_PREFS_DIR = "shared_prefs";
59     private static final String SHARED_PREFS_NAME = "cec_config.xml";
60 
61     private static final int STORAGE_SYSPROPS = 0;
62     private static final int STORAGE_GLOBAL_SETTINGS = 1;
63     private static final int STORAGE_SHARED_PREFS = 2;
64 
65     @IntDef({
66         STORAGE_SYSPROPS,
67         STORAGE_GLOBAL_SETTINGS,
68         STORAGE_SHARED_PREFS,
69     })
70     private @interface Storage {}
71 
72     private static final String VALUE_TYPE_STRING = "string";
73     private static final String VALUE_TYPE_INT = "int";
74 
75     @StringDef({
76         VALUE_TYPE_STRING,
77         VALUE_TYPE_INT,
78     })
79     private @interface ValueType {}
80 
81     @NonNull private final Context mContext;
82     @NonNull private final StorageAdapter mStorageAdapter;
83 
84     private final Object mLock = new Object();
85 
86     @GuardedBy("mLock")
87     private final ArrayMap<Setting, ArrayMap<SettingChangeListener, Executor>>
88             mSettingChangeListeners = new ArrayMap<>();
89 
90     private LinkedHashMap<String, Setting> mSettings = new LinkedHashMap<>();
91 
92     /**
93      * Exception thrown when the CEC Configuration setup verification fails.
94      * This usually means a settings lacks default value or storage/storage key.
95      */
96     public static class VerificationException extends RuntimeException {
VerificationException(String message)97         public VerificationException(String message) {
98             super(message);
99         }
100     }
101 
102     /**
103      * Listener used to get notifications when value of a setting changes.
104      */
105     public interface SettingChangeListener {
106         /**
107          * Called when value of a setting changes.
108          *
109          * @param setting name of a CEC setting that changed
110          */
onChange(@onNull @ettingName String setting)111         void onChange(@NonNull @SettingName String setting);
112     }
113 
114     /**
115      * Setting storage input/output helper class.
116      */
117     public static class StorageAdapter {
118         @NonNull private final Context mContext;
119         @NonNull private final SharedPreferences mSharedPrefs;
120 
StorageAdapter(@onNull Context context)121         StorageAdapter(@NonNull Context context) {
122             mContext = context;
123             // The package info in the context isn't initialized in the way it is for normal apps,
124             // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
125             // build the path manually below using the same policy that appears in ContextImpl.
126             final Context deviceContext = mContext.createDeviceProtectedStorageContext();
127             final File prefsFile = new File(new File(Environment.getDataSystemDirectory(),
128                                                      SHARED_PREFS_DIR), SHARED_PREFS_NAME);
129             mSharedPrefs = deviceContext.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
130         }
131 
132         /**
133          * Read the value from a system property.
134          * Returns the given default value if the system property is not set.
135          */
retrieveSystemProperty(@onNull String storageKey, @NonNull String defaultValue)136         public String retrieveSystemProperty(@NonNull String storageKey,
137                                              @NonNull String defaultValue) {
138             return SystemProperties.get(storageKey, defaultValue);
139         }
140 
141         /**
142          * Write the value to a system property.
143          */
storeSystemProperty(@onNull String storageKey, @NonNull String value)144         public void storeSystemProperty(@NonNull String storageKey,
145                                         @NonNull String value) {
146             SystemProperties.set(storageKey, value);
147         }
148 
149         /**
150          * Read the value from a global setting.
151          * Returns the given default value if the system property is not set.
152          */
retrieveGlobalSetting(@onNull String storageKey, @NonNull String defaultValue)153         public String retrieveGlobalSetting(@NonNull String storageKey,
154                                             @NonNull String defaultValue) {
155             String value = Global.getString(mContext.getContentResolver(), storageKey);
156             return value != null ? value : defaultValue;
157         }
158 
159         /**
160          * Write the value to a global setting.
161          */
storeGlobalSetting(@onNull String storageKey, @NonNull String value)162         public void storeGlobalSetting(@NonNull String storageKey,
163                                        @NonNull String value) {
164             Global.putString(mContext.getContentResolver(), storageKey, value);
165         }
166 
167         /**
168          * Read the value from a shared preference.
169          * Returns the given default value if the preference is not set.
170          */
retrieveSharedPref(@onNull String storageKey, @NonNull String defaultValue)171         public String retrieveSharedPref(@NonNull String storageKey,
172                                          @NonNull String defaultValue) {
173             return mSharedPrefs.getString(storageKey, defaultValue);
174         }
175 
176         /**
177          * Write the value to a shared preference.
178          */
storeSharedPref(@onNull String storageKey, @NonNull String value)179         public void storeSharedPref(@NonNull String storageKey,
180                                     @NonNull String value) {
181             mSharedPrefs.edit().putString(storageKey, value).apply();
182         }
183     }
184 
185     private class Value {
186         private final String mStringValue;
187         private final Integer mIntValue;
188 
Value(@onNull String value)189         Value(@NonNull String value) {
190             mStringValue = value;
191             mIntValue = null;
192         }
193 
Value(@onNull Integer value)194         Value(@NonNull Integer value) {
195             mStringValue = null;
196             mIntValue = value;
197         }
198 
getStringValue()199         String getStringValue() {
200             return mStringValue;
201         }
202 
getIntValue()203         Integer getIntValue() {
204             return mIntValue;
205         }
206     }
207 
208     protected class Setting {
209         @NonNull private final Context mContext;
210         @NonNull private final @SettingName String mName;
211         private final boolean mUserConfigurable;
212 
213         private Value mDefaultValue = null;
214         private List<Value> mAllowedValues = new ArrayList<>();
215 
Setting(@onNull Context context, @NonNull @SettingName String name, int userConfResId)216         Setting(@NonNull Context context,
217                 @NonNull @SettingName String name,
218                 int userConfResId) {
219             mContext = context;
220             mName = name;
221             mUserConfigurable = mContext.getResources().getBoolean(userConfResId);
222         }
223 
getName()224         public @SettingName String getName() {
225             return mName;
226         }
227 
getValueType()228         public @ValueType String getValueType() {
229             return getDefaultValue().getStringValue() != null
230                     ? VALUE_TYPE_STRING
231                     : VALUE_TYPE_INT;
232         }
233 
getDefaultValue()234         public Value getDefaultValue() {
235             if (mDefaultValue == null) {
236                 throw new VerificationException("Invalid CEC setup for '"
237                     + this.getName() + "' setting. "
238                     + "Setting has no default value.");
239             }
240             return mDefaultValue;
241         }
242 
getUserConfigurable()243         public boolean getUserConfigurable() {
244             return mUserConfigurable;
245         }
246 
registerValue(@onNull Value value, int allowedResId, int defaultResId)247         private void registerValue(@NonNull Value value,
248                                    int allowedResId, int defaultResId) {
249             if (mContext.getResources().getBoolean(allowedResId)) {
250                 mAllowedValues.add(value);
251                 if (mContext.getResources().getBoolean(defaultResId)) {
252                     if (mDefaultValue != null) {
253                         Slog.e(TAG,
254                                 "Failed to set '" + value + "' as a default for '" + this.getName()
255                                         + "': Setting already has a default ('" + mDefaultValue
256                                         + "').");
257                         return;
258                     }
259                     mDefaultValue = value;
260                 }
261             }
262         }
263 
registerValue(@onNull String value, int allowedResId, int defaultResId)264         public void registerValue(@NonNull String value, int allowedResId,
265                                   int defaultResId) {
266             registerValue(new Value(value), allowedResId, defaultResId);
267         }
268 
registerValue(int value, int allowedResId, int defaultResId)269         public void registerValue(int value, int allowedResId,
270                                   int defaultResId) {
271             registerValue(new Value(value), allowedResId, defaultResId);
272         }
273 
274 
getAllowedValues()275         public List<Value> getAllowedValues() {
276             return mAllowedValues;
277         }
278     }
279 
280     @VisibleForTesting
HdmiCecConfig(@onNull Context context, @NonNull StorageAdapter storageAdapter)281     HdmiCecConfig(@NonNull Context context,
282                   @NonNull StorageAdapter storageAdapter) {
283         mContext = context;
284         mStorageAdapter = storageAdapter;
285 
286         // IMPORTANT: when adding a config value for a particular setting, register that value AFTER
287         // the existing values for that setting. That way, defaults set in the RRO are forward
288         // compatible even if the RRO doesn't include that new value yet
289         // (e.g. because it's ported from a previous release).
290 
291         Setting hdmiCecEnabled = registerSetting(
292                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
293                 R.bool.config_cecHdmiCecEnabled_userConfigurable);
294         hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED,
295                 R.bool.config_cecHdmiCecControlEnabled_allowed,
296                 R.bool.config_cecHdmiCecControlEnabled_default);
297         hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
298                 R.bool.config_cecHdmiCecControlDisabled_allowed,
299                 R.bool.config_cecHdmiCecControlDisabled_default);
300 
301         Setting hdmiCecVersion = registerSetting(
302                 HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
303                 R.bool.config_cecHdmiCecVersion_userConfigurable);
304         hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
305                 R.bool.config_cecHdmiCecVersion14b_allowed,
306                 R.bool.config_cecHdmiCecVersion14b_default);
307         hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_2_0,
308                 R.bool.config_cecHdmiCecVersion20_allowed,
309                 R.bool.config_cecHdmiCecVersion20_default);
310 
311         Setting routingControlControl = registerSetting(
312                 HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
313                 R.bool.config_cecRoutingControl_userConfigurable);
314         routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_ENABLED,
315                 R.bool.config_cecRoutingControlEnabled_allowed,
316                 R.bool.config_cecRoutingControlEnabled_default);
317         routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_DISABLED,
318                 R.bool.config_cecRoutingControlDisabled_allowed,
319                 R.bool.config_cecRoutingControlDisabled_default);
320 
321         Setting soundbarMode = registerSetting(
322                 HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE,
323                 R.bool.config_cecSoundbarMode_userConfigurable);
324         soundbarMode.registerValue(HdmiControlManager.SOUNDBAR_MODE_ENABLED,
325                 R.bool.config_cecSoundbarModeEnabled_allowed,
326                 R.bool.config_cecSoundbarModeEnabled_default);
327         soundbarMode.registerValue(HdmiControlManager.SOUNDBAR_MODE_DISABLED,
328                 R.bool.config_cecSoundbarModeDisabled_allowed,
329                 R.bool.config_cecSoundbarModeDisabled_default);
330 
331         Setting powerControlMode = registerSetting(
332                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
333                 R.bool.config_cecPowerControlMode_userConfigurable);
334         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
335                 R.bool.config_cecPowerControlModeTv_allowed,
336                 R.bool.config_cecPowerControlModeTv_default);
337         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
338                 R.bool.config_cecPowerControlModeBroadcast_allowed,
339                 R.bool.config_cecPowerControlModeBroadcast_default);
340         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
341                 R.bool.config_cecPowerControlModeNone_allowed,
342                 R.bool.config_cecPowerControlModeNone_default);
343         powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
344                 R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
345                 R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
346 
347         Setting powerStateChangeOnActiveSourceLost = registerSetting(
348                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
349                 R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
350         powerStateChangeOnActiveSourceLost.registerValue(
351                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
352                 R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed,
353                 R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
354         powerStateChangeOnActiveSourceLost.registerValue(
355                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW,
356                 R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
357                 R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
358 
359         Setting systemAudioControl = registerSetting(
360                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
361                 R.bool.config_cecSystemAudioControl_userConfigurable);
362         systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED,
363                 R.bool.config_cecSystemAudioControlEnabled_allowed,
364                 R.bool.config_cecSystemAudioControlEnabled_default);
365         systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_DISABLED,
366                 R.bool.config_cecSystemAudioControlDisabled_allowed,
367                 R.bool.config_cecSystemAudioControlDisabled_default);
368 
369         Setting systemAudioModeMuting = registerSetting(
370                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
371                 R.bool.config_cecSystemAudioModeMuting_userConfigurable);
372         systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED,
373                 R.bool.config_cecSystemAudioModeMutingEnabled_allowed,
374                 R.bool.config_cecSystemAudioModeMutingEnabled_default);
375         systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED,
376                 R.bool.config_cecSystemAudioModeMutingDisabled_allowed,
377                 R.bool.config_cecSystemAudioModeMutingDisabled_default);
378 
379         Setting volumeControlMode = registerSetting(
380                 HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
381                 R.bool.config_cecVolumeControlMode_userConfigurable);
382         volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_ENABLED,
383                 R.bool.config_cecVolumeControlModeEnabled_allowed,
384                 R.bool.config_cecVolumeControlModeEnabled_default);
385         volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_DISABLED,
386                 R.bool.config_cecVolumeControlModeDisabled_allowed,
387                 R.bool.config_cecVolumeControlModeDisabled_default);
388 
389         Setting tvWakeOnOneTouchPlay = registerSetting(
390                 HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
391                 R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
392         tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED,
393                 R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed,
394                 R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
395         tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED,
396                 R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed,
397                 R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
398 
399         Setting tvSendStandbyOnSleep = registerSetting(
400                 HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
401                 R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
402         tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED,
403                 R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed,
404                 R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
405         tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED,
406                 R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
407                 R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
408 
409         Setting setMenuLanguage = registerSetting(
410                 HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
411                 R.bool.config_cecSetMenuLanguage_userConfigurable);
412         setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_ENABLED,
413                 R.bool.config_cecSetMenuLanguageEnabled_allowed,
414                 R.bool.config_cecSetMenuLanguageEnabled_default);
415         setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_DISABLED,
416                 R.bool.config_cecSetMenuLanguageDisabled_allowed,
417                 R.bool.config_cecSetMenuLanguageDisabled_default);
418 
419         Setting rcProfileTv = registerSetting(
420                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
421                 R.bool.config_cecRcProfileTv_userConfigurable);
422         rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_NONE,
423                 R.bool.config_cecRcProfileTvNone_allowed,
424                 R.bool.config_cecRcProfileTvNone_default);
425         rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_ONE,
426                 R.bool.config_cecRcProfileTvOne_allowed,
427                 R.bool.config_cecRcProfileTvOne_default);
428         rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_TWO,
429                 R.bool.config_cecRcProfileTvTwo_allowed,
430                 R.bool.config_cecRcProfileTvTwo_default);
431         rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_THREE,
432                 R.bool.config_cecRcProfileTvThree_allowed,
433                 R.bool.config_cecRcProfileTvThree_default);
434         rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_FOUR,
435                 R.bool.config_cecRcProfileTvFour_allowed,
436                 R.bool.config_cecRcProfileTvFour_default);
437 
438         Setting rcProfileSourceRootMenu = registerSetting(
439                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
440                 R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
441         rcProfileSourceRootMenu.registerValue(
442                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
443                 R.bool.config_cecRcProfileSourceRootMenuHandled_allowed,
444                 R.bool.config_cecRcProfileSourceRootMenuHandled_default);
445         rcProfileSourceRootMenu.registerValue(
446                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
447                 R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed,
448                 R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
449 
450         Setting rcProfileSourceSetupMenu = registerSetting(
451                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
452                 R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
453         rcProfileSourceSetupMenu.registerValue(
454                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
455                 R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed,
456                 R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
457         rcProfileSourceSetupMenu.registerValue(
458                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
459                 R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed,
460                 R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
461 
462         Setting rcProfileSourceContentsMenu = registerSetting(
463                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
464                 R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
465         rcProfileSourceContentsMenu.registerValue(
466                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
467                 R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed,
468                 R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
469         rcProfileSourceContentsMenu.registerValue(
470                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
471                 R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed,
472                 R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
473 
474         Setting rcProfileSourceTopMenu = registerSetting(
475                 HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
476                 R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
477         rcProfileSourceTopMenu.registerValue(
478                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
479                 R.bool.config_cecRcProfileSourceTopMenuHandled_allowed,
480                 R.bool.config_cecRcProfileSourceTopMenuHandled_default);
481         rcProfileSourceTopMenu.registerValue(
482                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
483                 R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed,
484                 R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
485 
486         Setting rcProfileSourceMediaContextSensitiveMenu = registerSetting(
487                 HdmiControlManager
488                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
489                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
490         rcProfileSourceMediaContextSensitiveMenu.registerValue(
491                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_HANDLED,
492                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed,
493                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
494         rcProfileSourceMediaContextSensitiveMenu.registerValue(
495                 HdmiControlManager.RC_PROFILE_SOURCE_MENU_NOT_HANDLED,
496                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed,
497                 R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
498 
499         Setting querySadLpcm = registerSetting(
500                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM,
501                 R.bool.config_cecQuerySadLpcm_userConfigurable);
502         querySadLpcm.registerValue(
503                 HdmiControlManager.QUERY_SAD_ENABLED,
504                 R.bool.config_cecQuerySadLpcmEnabled_allowed,
505                 R.bool.config_cecQuerySadLpcmEnabled_default);
506         querySadLpcm.registerValue(
507                 HdmiControlManager.QUERY_SAD_DISABLED,
508                 R.bool.config_cecQuerySadLpcmDisabled_allowed,
509                 R.bool.config_cecQuerySadLpcmDisabled_default);
510 
511         Setting querySadDd = registerSetting(
512                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD,
513                 R.bool.config_cecQuerySadDd_userConfigurable);
514         querySadDd.registerValue(
515                 HdmiControlManager.QUERY_SAD_ENABLED,
516                 R.bool.config_cecQuerySadDdEnabled_allowed,
517                 R.bool.config_cecQuerySadDdEnabled_default);
518         querySadDd.registerValue(
519                 HdmiControlManager.QUERY_SAD_DISABLED,
520                 R.bool.config_cecQuerySadDdDisabled_allowed,
521                 R.bool.config_cecQuerySadDdDisabled_default);
522 
523         Setting querySadMpeg1 = registerSetting(
524                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
525                 R.bool.config_cecQuerySadMpeg1_userConfigurable);
526         querySadMpeg1.registerValue(
527                 HdmiControlManager.QUERY_SAD_ENABLED,
528                 R.bool.config_cecQuerySadMpeg1Enabled_allowed,
529                 R.bool.config_cecQuerySadMpeg1Enabled_default);
530         querySadMpeg1.registerValue(
531                 HdmiControlManager.QUERY_SAD_DISABLED,
532                 R.bool.config_cecQuerySadMpeg1Disabled_allowed,
533                 R.bool.config_cecQuerySadMpeg1Disabled_default);
534 
535         Setting querySadMp3 = registerSetting(
536                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3,
537                 R.bool.config_cecQuerySadMp3_userConfigurable);
538         querySadMp3.registerValue(
539                 HdmiControlManager.QUERY_SAD_ENABLED,
540                 R.bool.config_cecQuerySadMp3Enabled_allowed,
541                 R.bool.config_cecQuerySadMp3Enabled_default);
542         querySadMp3.registerValue(
543                 HdmiControlManager.QUERY_SAD_DISABLED,
544                 R.bool.config_cecQuerySadMp3Disabled_allowed,
545                 R.bool.config_cecQuerySadMp3Disabled_default);
546 
547         Setting querySadMpeg2 = registerSetting(
548                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2,
549                 R.bool.config_cecQuerySadMpeg2_userConfigurable);
550         querySadMpeg2.registerValue(
551                 HdmiControlManager.QUERY_SAD_ENABLED,
552                 R.bool.config_cecQuerySadMpeg2Enabled_allowed,
553                 R.bool.config_cecQuerySadMpeg2Enabled_default);
554         querySadMpeg2.registerValue(
555                 HdmiControlManager.QUERY_SAD_DISABLED,
556                 R.bool.config_cecQuerySadMpeg2Disabled_allowed,
557                 R.bool.config_cecQuerySadMpeg2Disabled_default);
558 
559         Setting querySadAac = registerSetting(
560                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
561                 R.bool.config_cecQuerySadAac_userConfigurable);
562         querySadAac.registerValue(
563                 HdmiControlManager.QUERY_SAD_ENABLED,
564                 R.bool.config_cecQuerySadAacEnabled_allowed,
565                 R.bool.config_cecQuerySadAacEnabled_default);
566         querySadAac.registerValue(
567                 HdmiControlManager.QUERY_SAD_DISABLED,
568                 R.bool.config_cecQuerySadAacDisabled_allowed,
569                 R.bool.config_cecQuerySadAacDisabled_default);
570 
571         Setting querySadDts = registerSetting(
572                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
573                 R.bool.config_cecQuerySadDts_userConfigurable);
574         querySadDts.registerValue(
575                 HdmiControlManager.QUERY_SAD_ENABLED,
576                 R.bool.config_cecQuerySadDtsEnabled_allowed,
577                 R.bool.config_cecQuerySadDtsEnabled_default);
578         querySadDts.registerValue(
579                 HdmiControlManager.QUERY_SAD_DISABLED,
580                 R.bool.config_cecQuerySadDtsDisabled_allowed,
581                 R.bool.config_cecQuerySadDtsDisabled_default);
582 
583         Setting querySadAtrac = registerSetting(
584                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
585                 R.bool.config_cecQuerySadAtrac_userConfigurable);
586         querySadAtrac.registerValue(
587                 HdmiControlManager.QUERY_SAD_ENABLED,
588                 R.bool.config_cecQuerySadAtracEnabled_allowed,
589                 R.bool.config_cecQuerySadAtracEnabled_default);
590         querySadAtrac.registerValue(
591                 HdmiControlManager.QUERY_SAD_DISABLED,
592                 R.bool.config_cecQuerySadAtracDisabled_allowed,
593                 R.bool.config_cecQuerySadAtracDisabled_default);
594 
595         Setting querySadOnebitaudio = registerSetting(
596                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
597                 R.bool.config_cecQuerySadOnebitaudio_userConfigurable);
598         querySadOnebitaudio.registerValue(
599                 HdmiControlManager.QUERY_SAD_ENABLED,
600                 R.bool.config_cecQuerySadOnebitaudioEnabled_allowed,
601                 R.bool.config_cecQuerySadOnebitaudioEnabled_default);
602         querySadOnebitaudio.registerValue(
603                 HdmiControlManager.QUERY_SAD_DISABLED,
604                 R.bool.config_cecQuerySadOnebitaudioDisabled_allowed,
605                 R.bool.config_cecQuerySadOnebitaudioDisabled_default);
606 
607         Setting querySadDdp = registerSetting(
608                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
609                 R.bool.config_cecQuerySadDdp_userConfigurable);
610         querySadDdp.registerValue(
611                 HdmiControlManager.QUERY_SAD_ENABLED,
612                 R.bool.config_cecQuerySadDdpEnabled_allowed,
613                 R.bool.config_cecQuerySadDdpEnabled_default);
614         querySadDdp.registerValue(
615                 HdmiControlManager.QUERY_SAD_DISABLED,
616                 R.bool.config_cecQuerySadDdpDisabled_allowed,
617                 R.bool.config_cecQuerySadDdpDisabled_default);
618 
619         Setting querySadDtshd = registerSetting(
620                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
621                 R.bool.config_cecQuerySadDtshd_userConfigurable);
622         querySadDtshd.registerValue(
623                 HdmiControlManager.QUERY_SAD_ENABLED,
624                 R.bool.config_cecQuerySadDtshdEnabled_allowed,
625                 R.bool.config_cecQuerySadDtshdEnabled_default);
626         querySadDtshd.registerValue(
627                 HdmiControlManager.QUERY_SAD_DISABLED,
628                 R.bool.config_cecQuerySadDtshdDisabled_allowed,
629                 R.bool.config_cecQuerySadDtshdDisabled_default);
630 
631         Setting querySadTruehd = registerSetting(
632                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
633                 R.bool.config_cecQuerySadTruehd_userConfigurable);
634         querySadTruehd.registerValue(
635                 HdmiControlManager.QUERY_SAD_ENABLED,
636                 R.bool.config_cecQuerySadTruehdEnabled_allowed,
637                 R.bool.config_cecQuerySadTruehdEnabled_default);
638         querySadTruehd.registerValue(
639                 HdmiControlManager.QUERY_SAD_DISABLED,
640                 R.bool.config_cecQuerySadTruehdDisabled_allowed,
641                 R.bool.config_cecQuerySadTruehdDisabled_default);
642 
643         Setting querySadDst = registerSetting(
644                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
645                 R.bool.config_cecQuerySadDst_userConfigurable);
646         querySadDst.registerValue(
647                 HdmiControlManager.QUERY_SAD_ENABLED,
648                 R.bool.config_cecQuerySadDstEnabled_allowed,
649                 R.bool.config_cecQuerySadDstEnabled_default);
650         querySadDst.registerValue(
651                 HdmiControlManager.QUERY_SAD_DISABLED,
652                 R.bool.config_cecQuerySadDstDisabled_allowed,
653                 R.bool.config_cecQuerySadDstDisabled_default);
654 
655         Setting querySadWmapro = registerSetting(
656                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
657                 R.bool.config_cecQuerySadWmapro_userConfigurable);
658         querySadWmapro.registerValue(
659                 HdmiControlManager.QUERY_SAD_ENABLED,
660                 R.bool.config_cecQuerySadWmaproEnabled_allowed,
661                 R.bool.config_cecQuerySadWmaproEnabled_default);
662         querySadWmapro.registerValue(
663                 HdmiControlManager.QUERY_SAD_DISABLED,
664                 R.bool.config_cecQuerySadWmaproDisabled_allowed,
665                 R.bool.config_cecQuerySadWmaproDisabled_default);
666 
667         Setting querySadMax = registerSetting(
668                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX,
669                 R.bool.config_cecQuerySadMax_userConfigurable);
670         querySadMax.registerValue(
671                 HdmiControlManager.QUERY_SAD_ENABLED,
672                 R.bool.config_cecQuerySadMaxEnabled_allowed,
673                 R.bool.config_cecQuerySadMaxEnabled_default);
674         querySadMax.registerValue(
675                 HdmiControlManager.QUERY_SAD_DISABLED,
676                 R.bool.config_cecQuerySadMaxDisabled_allowed,
677                 R.bool.config_cecQuerySadMaxDisabled_default);
678 
679         Setting earcEnabled = registerSetting(
680                 HdmiControlManager.SETTING_NAME_EARC_ENABLED,
681                 R.bool.config_earcEnabled_userConfigurable);
682         earcEnabled.registerValue(HdmiControlManager.EARC_FEATURE_ENABLED,
683                 R.bool.config_earcFeatureEnabled_allowed,
684                 R.bool.config_earcFeatureEnabled_default);
685         earcEnabled.registerValue(HdmiControlManager.EARC_FEATURE_DISABLED,
686                 R.bool.config_earcFeatureDisabled_allowed,
687                 R.bool.config_earcFeatureDisabled_default);
688 
689         verifySettings();
690     }
691 
HdmiCecConfig(@onNull Context context)692     HdmiCecConfig(@NonNull Context context) {
693         this(context, new StorageAdapter(context));
694     }
695 
registerSetting(@onNull @ettingName String name, int userConfResId)696     private Setting registerSetting(@NonNull @SettingName String name,
697                                int userConfResId) {
698         Setting setting = new Setting(mContext, name, userConfResId);
699         mSettings.put(name, setting);
700         return setting;
701     }
702 
verifySettings()703     private void verifySettings() {
704         for (Setting setting: mSettings.values()) {
705             // This will throw an exception when a setting
706             // doesn't have a default value assigned.
707             setting.getDefaultValue();
708             getStorage(setting);
709             getStorageKey(setting);
710         }
711     }
712 
713     @Nullable
getSetting(@onNull String name)714     private Setting getSetting(@NonNull String name) {
715         return mSettings.containsKey(name) ? mSettings.get(name) : null;
716     }
717 
718     @Storage
getStorage(@onNull Setting setting)719     private int getStorage(@NonNull Setting setting) {
720         switch (setting.getName()) {
721             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
722                 return STORAGE_SHARED_PREFS;
723             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
724                 return STORAGE_SHARED_PREFS;
725             case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
726                 return STORAGE_SHARED_PREFS;
727             case HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE:
728                 return STORAGE_SHARED_PREFS;
729             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
730                 return STORAGE_SHARED_PREFS;
731             case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
732                 return STORAGE_SHARED_PREFS;
733             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
734                 return STORAGE_SHARED_PREFS;
735             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
736                 return STORAGE_SHARED_PREFS;
737             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
738                 return STORAGE_SHARED_PREFS;
739             case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
740                 return STORAGE_SHARED_PREFS;
741             case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
742                 return STORAGE_SHARED_PREFS;
743             case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
744                 return STORAGE_SHARED_PREFS;
745             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
746                 return STORAGE_SHARED_PREFS;
747             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
748                 return STORAGE_SHARED_PREFS;
749             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU:
750                 return STORAGE_SHARED_PREFS;
751             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU:
752                 return STORAGE_SHARED_PREFS;
753             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU:
754                 return STORAGE_SHARED_PREFS;
755             case HdmiControlManager
756                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
757                 return STORAGE_SHARED_PREFS;
758             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM:
759                 return STORAGE_SHARED_PREFS;
760             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD:
761                 return STORAGE_SHARED_PREFS;
762             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1:
763                 return STORAGE_SHARED_PREFS;
764             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3:
765                 return STORAGE_SHARED_PREFS;
766             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2:
767                 return STORAGE_SHARED_PREFS;
768             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC:
769                 return STORAGE_SHARED_PREFS;
770             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS:
771                 return STORAGE_SHARED_PREFS;
772             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC:
773                 return STORAGE_SHARED_PREFS;
774             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO:
775                 return STORAGE_SHARED_PREFS;
776             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP:
777                 return STORAGE_SHARED_PREFS;
778             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD:
779                 return STORAGE_SHARED_PREFS;
780             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD:
781                 return STORAGE_SHARED_PREFS;
782             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST:
783                 return STORAGE_SHARED_PREFS;
784             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO:
785                 return STORAGE_SHARED_PREFS;
786             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
787                 return STORAGE_SHARED_PREFS;
788             case HdmiControlManager.SETTING_NAME_EARC_ENABLED:
789                 return STORAGE_SHARED_PREFS;
790             default:
791                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
792                         + "' storage.");
793         }
794     }
795 
getStorageKey(@onNull Setting setting)796     private String getStorageKey(@NonNull Setting setting) {
797         switch (setting.getName()) {
798             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED:
799                 return setting.getName();
800             case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
801                 return setting.getName();
802             case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
803                 return setting.getName();
804             case HdmiControlManager.CEC_SETTING_NAME_SOUNDBAR_MODE:
805                 return setting.getName();
806             case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
807                 return setting.getName();
808             case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
809                 return setting.getName();
810             case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
811                 return setting.getName();
812             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
813                 return setting.getName();
814             case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
815                 return setting.getName();
816             case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
817                 return setting.getName();
818             case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
819                 return setting.getName();
820             case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
821                 return setting.getName();
822             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
823                 return setting.getName();
824             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
825                 return setting.getName();
826             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU:
827                 return setting.getName();
828             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU:
829                 return setting.getName();
830             case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU:
831                 return setting.getName();
832             case HdmiControlManager
833                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
834                 return setting.getName();
835             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_LPCM:
836                 return setting.getName();
837             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DD:
838                 return setting.getName();
839             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1:
840                 return setting.getName();
841             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MP3:
842                 return setting.getName();
843             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG2:
844                 return setting.getName();
845             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC:
846                 return setting.getName();
847             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS:
848                 return setting.getName();
849             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC:
850                 return setting.getName();
851             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO:
852                 return setting.getName();
853             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP:
854                 return setting.getName();
855             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD:
856                 return setting.getName();
857             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD:
858                 return setting.getName();
859             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST:
860                 return setting.getName();
861             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO:
862                 return setting.getName();
863             case HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX:
864                 return setting.getName();
865             case HdmiControlManager.SETTING_NAME_EARC_ENABLED:
866                 return setting.getName();
867             default:
868                 throw new VerificationException("Invalid CEC setting '" + setting.getName()
869                     + "' storage key.");
870         }
871     }
872 
retrieveValue(@onNull Setting setting, @NonNull String defaultValue)873     protected String retrieveValue(@NonNull Setting setting, @NonNull String defaultValue) {
874         @Storage int storage = getStorage(setting);
875         String storageKey = getStorageKey(setting);
876         if (storage == STORAGE_SYSPROPS) {
877             HdmiLogger.debug("Reading '" + storageKey + "' sysprop.");
878             return mStorageAdapter.retrieveSystemProperty(storageKey, defaultValue);
879         } else if (storage == STORAGE_GLOBAL_SETTINGS) {
880             HdmiLogger.debug("Reading '" + storageKey + "' global setting.");
881             return mStorageAdapter.retrieveGlobalSetting(storageKey, defaultValue);
882         } else if (storage == STORAGE_SHARED_PREFS) {
883             HdmiLogger.debug("Reading '" + storageKey + "' shared preference.");
884             return mStorageAdapter.retrieveSharedPref(storageKey, defaultValue);
885         }
886         return null;
887     }
888 
storeValue(@onNull Setting setting, @NonNull String value)889     protected void storeValue(@NonNull Setting setting, @NonNull String value) {
890         @Storage int storage = getStorage(setting);
891         String storageKey = getStorageKey(setting);
892         if (storage == STORAGE_SYSPROPS) {
893             HdmiLogger.debug("Setting '" + storageKey + "' sysprop.");
894             mStorageAdapter.storeSystemProperty(storageKey, value);
895         } else if (storage == STORAGE_GLOBAL_SETTINGS) {
896             HdmiLogger.debug("Setting '" + storageKey + "' global setting.");
897             mStorageAdapter.storeGlobalSetting(storageKey, value);
898         } else if (storage == STORAGE_SHARED_PREFS) {
899             HdmiLogger.debug("Setting '" + storageKey + "' shared pref.");
900             mStorageAdapter.storeSharedPref(storageKey, value);
901             notifySettingChanged(setting);
902         }
903     }
904 
notifySettingChanged(@onNull Setting setting)905     protected void notifySettingChanged(@NonNull Setting setting) {
906         synchronized (mLock) {
907             ArrayMap<SettingChangeListener, Executor> listeners =
908                     mSettingChangeListeners.get(setting);
909             if (listeners == null) {
910                 return;  // No listeners registered, do nothing.
911             }
912             for (Entry<SettingChangeListener, Executor> entry: listeners.entrySet()) {
913                 SettingChangeListener listener = entry.getKey();
914                 Executor executor = entry.getValue();
915                 executor.execute(new Runnable() {
916                     @Override
917                     public void run() {
918                         listener.onChange(setting.getName());
919                     }
920                 });
921             }
922         }
923     }
924 
925     /**
926      * Register change listener for a given setting name using DirectExecutor.
927      */
registerChangeListener(@onNull @ettingName String name, SettingChangeListener listener)928     public void registerChangeListener(@NonNull @SettingName String name,
929                                        SettingChangeListener listener) {
930         registerChangeListener(name, listener, ConcurrentUtils.DIRECT_EXECUTOR);
931     }
932 
933     /**
934      * Register change listener for a given setting name and executor.
935      */
registerChangeListener(@onNull @ettingName String name, SettingChangeListener listener, Executor executor)936     public void registerChangeListener(@NonNull @SettingName String name,
937                                        SettingChangeListener listener,
938                                        Executor executor) {
939         Setting setting = getSetting(name);
940         if (setting == null) {
941             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
942         }
943         @Storage int storage = getStorage(setting);
944         if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) {
945             throw new IllegalArgumentException("Change listeners for setting '" + name
946                     + "' not supported.");
947         }
948         synchronized (mLock) {
949             if (!mSettingChangeListeners.containsKey(setting)) {
950                 mSettingChangeListeners.put(setting, new ArrayMap<>());
951             }
952             mSettingChangeListeners.get(setting).put(listener, executor);
953         }
954     }
955 
956     /**
957      * Remove change listener for a given setting name.
958      */
removeChangeListener(@onNull @ettingName String name, SettingChangeListener listener)959     public void removeChangeListener(@NonNull @SettingName String name,
960                                      SettingChangeListener listener) {
961         Setting setting = getSetting(name);
962         if (setting == null) {
963             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
964         }
965         synchronized (mLock) {
966             if (mSettingChangeListeners.containsKey(setting)) {
967                 ArrayMap<SettingChangeListener, Executor> listeners =
968                         mSettingChangeListeners.get(setting);
969                 listeners.remove(listener);
970                 if (listeners.isEmpty()) {
971                     mSettingChangeListeners.remove(setting);
972                 }
973             }
974         }
975     }
976 
977     /**
978      * Returns a list of all settings based on the XML metadata.
979      */
getAllSettings()980     public @SettingName List<String> getAllSettings() {
981         return new ArrayList<>(mSettings.keySet());
982     }
983 
984     /**
985      * Returns a list of user-modifiable settings based on the XML metadata.
986      */
getUserSettings()987     public @SettingName List<String> getUserSettings() {
988         List<String> settings = new ArrayList<>();
989         for (Setting setting: mSettings.values()) {
990             if (setting.getUserConfigurable()) {
991                 settings.add(setting.getName());
992             }
993         }
994         return settings;
995     }
996 
997     /**
998      * For a given setting name returns true if and only if the value type of that
999      * setting is a string.
1000      */
isStringValueType(@onNull @ettingName String name)1001     public boolean isStringValueType(@NonNull @SettingName String name) {
1002         Setting setting = getSetting(name);
1003         if (setting == null) {
1004             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1005         }
1006         return getSetting(name).getValueType().equals(VALUE_TYPE_STRING);
1007     }
1008 
1009     /**
1010      * For a given setting name returns true if and only if the value type of that
1011      * setting is an int.
1012      */
isIntValueType(@onNull @ettingName String name)1013     public boolean isIntValueType(@NonNull @SettingName String name) {
1014         Setting setting = getSetting(name);
1015         if (setting == null) {
1016             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1017         }
1018         return getSetting(name).getValueType().equals(VALUE_TYPE_INT);
1019     }
1020 
1021     /**
1022      * For a given setting name returns values that are allowed for that setting (string).
1023      */
getAllowedStringValues(@onNull @ettingName String name)1024     public List<String> getAllowedStringValues(@NonNull @SettingName String name) {
1025         Setting setting = getSetting(name);
1026         if (setting == null) {
1027             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1028         }
1029         if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
1030             throw new IllegalArgumentException("Setting '" + name
1031                     + "' is not a string-type setting.");
1032         }
1033         List<String> allowedValues = new ArrayList<String>();
1034         for (Value allowedValue : setting.getAllowedValues()) {
1035             allowedValues.add(allowedValue.getStringValue());
1036         }
1037         return allowedValues;
1038     }
1039 
1040     /**
1041      * For a given setting name returns values that are allowed for that setting (string).
1042      */
getAllowedIntValues(@onNull @ettingName String name)1043     public List<Integer> getAllowedIntValues(@NonNull @SettingName String name) {
1044         Setting setting = getSetting(name);
1045         if (setting == null) {
1046             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1047         }
1048         if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
1049             throw new IllegalArgumentException("Setting '" + name
1050                     + "' is not a string-type setting.");
1051         }
1052         List<Integer> allowedValues = new ArrayList<Integer>();
1053         for (Value allowedValue : setting.getAllowedValues()) {
1054             allowedValues.add(allowedValue.getIntValue());
1055         }
1056         return allowedValues;
1057     }
1058 
1059     /**
1060      * For a given setting name returns the default value for that setting (string).
1061      */
getDefaultStringValue(@onNull @ettingName String name)1062     public String getDefaultStringValue(@NonNull @SettingName String name) {
1063         Setting setting = getSetting(name);
1064         if (setting == null) {
1065             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1066         }
1067         if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
1068             throw new IllegalArgumentException("Setting '" + name
1069                     + "' is not a string-type setting.");
1070         }
1071         return getSetting(name).getDefaultValue().getStringValue();
1072     }
1073 
1074     /**
1075      * For a given setting name returns the default value for that setting (int).
1076      */
getDefaultIntValue(@onNull @ettingName String name)1077     public int getDefaultIntValue(@NonNull @SettingName String name) {
1078         Setting setting = getSetting(name);
1079         if (setting == null) {
1080             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1081         }
1082         if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
1083             throw new IllegalArgumentException("Setting '" + name
1084                     + "' is not a string-type setting.");
1085         }
1086         return getSetting(name).getDefaultValue().getIntValue();
1087     }
1088 
1089     /**
1090      * For a given setting name returns the current value of that setting (string).
1091      */
getStringValue(@onNull @ettingName String name)1092     public String getStringValue(@NonNull @SettingName String name) {
1093         Setting setting = getSetting(name);
1094         if (setting == null) {
1095             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1096         }
1097         if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
1098             throw new IllegalArgumentException("Setting '" + name
1099                     + "' is not a string-type setting.");
1100         }
1101         HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
1102         return retrieveValue(setting, setting.getDefaultValue().getStringValue());
1103     }
1104 
1105     /**
1106      * For a given setting name returns the current value of that setting (int).
1107      */
getIntValue(@onNull @ettingName String name)1108     public int getIntValue(@NonNull @SettingName String name) {
1109         Setting setting = getSetting(name);
1110         if (setting == null) {
1111             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1112         }
1113         if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
1114             throw new IllegalArgumentException("Setting '" + name
1115                     + "' is not a int-type setting.");
1116         }
1117         HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
1118         String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue());
1119         String value = retrieveValue(setting, defaultValue);
1120         return Integer.parseInt(value);
1121     }
1122 
1123     /**
1124      * For a given setting name and value sets the current value of that setting (string).
1125      */
setStringValue(@onNull @ettingName String name, @NonNull String value)1126     public void setStringValue(@NonNull @SettingName String name, @NonNull String value) {
1127         Setting setting = getSetting(name);
1128         if (setting == null) {
1129             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1130         }
1131         if (!setting.getUserConfigurable()) {
1132             throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
1133         }
1134         if (!setting.getValueType().equals(VALUE_TYPE_STRING)) {
1135             throw new IllegalArgumentException("Setting '" + name
1136                     + "' is not a string-type setting.");
1137         }
1138         if (!getAllowedStringValues(name).contains(value)) {
1139             throw new IllegalArgumentException("Invalid CEC setting '" + name
1140                                                + "' value: '" + value + "'.");
1141         }
1142         HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'.");
1143         storeValue(setting, value);
1144     }
1145 
1146     /**
1147      * For a given setting name and value sets the current value of that setting (int).
1148      */
setIntValue(@onNull @ettingName String name, int value)1149     public void setIntValue(@NonNull @SettingName String name, int value) {
1150         Setting setting = getSetting(name);
1151         if (setting == null) {
1152             throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
1153         }
1154         if (!setting.getUserConfigurable()) {
1155             throw new IllegalArgumentException("Updating CEC setting '" + name + "' prohibited.");
1156         }
1157         if (!setting.getValueType().equals(VALUE_TYPE_INT)) {
1158             throw new IllegalArgumentException("Setting '" + name
1159                     + "' is not a int-type setting.");
1160         }
1161         if (!getAllowedIntValues(name).contains(value)) {
1162             throw new IllegalArgumentException("Invalid CEC setting '" + name
1163                                                + "' value: '" + value + "'.");
1164         }
1165         HdmiLogger.debug("Updating CEC setting '" + name + "' to '" + value + "'.");
1166         storeValue(setting, Integer.toString(value));
1167     }
1168 }
1169