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