Loading services/core/java/com/android/server/hdmi/HdmiCecConfig.java +134 −0 Original line number Diff line number Diff line Loading @@ -22,12 +22,19 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.database.ContentObserver; import android.hardware.hdmi.HdmiControlManager; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -88,6 +95,23 @@ public class HdmiCecConfig { @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; private final ArrayMap<Setting, Set<SettingChangeListener>> mSettingChangeListeners = new ArrayMap<>(); private SettingsObserver mSettingsObserver; /** * Listener used to get notifications when value of a setting changes. */ public interface SettingChangeListener { /** * Called when value of a setting changes. * * @param setting name of a CEC setting that changed */ void onChange(@NonNull @CecSettingName String setting); } /** * Setting storage input/output helper class. */ Loading Loading @@ -159,6 +183,18 @@ public class HdmiCecConfig { } } private class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange, Uri uri) { String setting = uri.getLastPathSegment(); HdmiCecConfig.this.notifyGlobalSettingChanged(setting); } } @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, Loading Loading @@ -311,6 +347,7 @@ public class HdmiCecConfig { } else if (storage == STORAGE_SHARED_PREFS) { Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); notifySettingChanged(setting); } } Loading @@ -318,6 +355,103 @@ public class HdmiCecConfig { return Integer.decode(value.getIntValue()); } private void notifyGlobalSettingChanged(String setting) { switch (setting) { case Global.HDMI_CONTROL_ENABLED: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); break; case Global.HDMI_CEC_VERSION: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); break; case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); break; } } private void notifySettingChanged(@NonNull @CecSettingName String name) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } notifySettingChanged(setting); } private void notifySettingChanged(@NonNull Setting setting) { Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); if (listeners == null) { return; // No listeners registered, do nothing. } for (SettingChangeListener listener: listeners) { listener.onChange(setting.getName()); } } /** * This method registers Global Setting change observer. * Needs to be called once after initialization of HdmiCecConfig. */ public void registerGlobalSettingsObserver(Looper looper) { Handler handler = new Handler(looper); mSettingsObserver = new SettingsObserver(handler); ContentResolver resolver = mContext.getContentResolver(); String[] settings = new String[] { Global.HDMI_CONTROL_ENABLED, Global.HDMI_CEC_VERSION, Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, }; for (String setting: settings) { resolver.registerContentObserver(Global.getUriFor(setting), false, mSettingsObserver, UserHandle.USER_ALL); } } /** * This method unregisters Global Setting change observer. */ public void unregisterGlobalSettingsObserver() { ContentResolver resolver = mContext.getContentResolver(); resolver.unregisterContentObserver(mSettingsObserver); } /** * Register change listener for a given setting name. */ public void registerChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } @Storage int storage = getStorage(setting); if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) { throw new IllegalArgumentException("Change listeners for setting '" + name + "' not supported."); } if (!mSettingChangeListeners.containsKey(setting)) { mSettingChangeListeners.put(setting, new HashSet<>()); } mSettingChangeListeners.get(setting).add(listener); } /** * Remove change listener for a given setting name. */ public void removeChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } if (mSettingChangeListeners.containsKey(setting)) { Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); listeners.remove(listener); if (listeners.isEmpty()) { mSettingChangeListeners.remove(setting); } } } /** * Returns a list of all settings based on the XML metadata. */ Loading services/core/java/com/android/server/hdmi/HdmiControlService.java +1 −0 Original line number Diff line number Diff line Loading @@ -496,6 +496,7 @@ public class HdmiControlService extends SystemService { if (mMessageValidator == null) { mMessageValidator = new HdmiCecMessageValidator(this); } mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper); } private void bootCompleted() { Loading services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +132 −0 Original line number Diff line number Diff line Loading @@ -18,13 +18,17 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.annotation.NonNull; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; Loading @@ -38,15 +42,23 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @SmallTest @Presubmit @RunWith(JUnit4.class) public final class HdmiCecConfigTest { private static final String TAG = "HdmiCecConfigTest"; private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; private final TestLooper mTestLooper = new TestLooper(); private Context mContext; @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; @Before public void setUp() throws Exception { Loading Loading @@ -1019,4 +1031,124 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); } @Test public void registerChangeListener_SharedPref_BasicSanity() { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"system_audio_mode_muting\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); verify(mSettingChangeListener).onChange( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); } @Test public void removeChangeListener_SharedPref_BasicSanity() { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"system_audio_mode_muting\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.removeChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); verify(mSettingChangeListener, never()).onChange( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); } /** * Externally modified Global Settings still need to be supported. This test verifies that * setting change notification is being forwarded to listeners registered via HdmiCecConfig. */ @Test public void globalSettingObserver_BasicSanity() throws Exception { CountDownLatch notifyLatch = new CountDownLatch(1); // Get current value of the setting in the system. String originalValue = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); try { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"hdmi_cec_enabled\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper()); HdmiCecConfig.SettingChangeListener latchUpdateListener = new HdmiCecConfig.SettingChangeListener() { @Override public void onChange( @NonNull @HdmiControlManager.CecSettingName String setting) { notifyLatch.countDown(); assertThat(setting).isEqualTo( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); } }; hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, latchUpdateListener); mTestLooper.dispatchAll(); // Flip the value of the setting. String valueToSet = ((originalValue == null || originalValue.equals("1")) ? "0" : "1"); Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, valueToSet); assertThat(Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED)).isEqualTo(valueToSet); // Write Setting a 2nd time as the listener doesn't always trigger on the first write // in the test. Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, valueToSet); mTestLooper.dispatchAll(); if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for the notify callback"); } hdmiCecConfig.unregisterGlobalSettingsObserver(); } finally { // Restore the previous value of the setting in the system. Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, originalValue); } } } Loading
services/core/java/com/android/server/hdmi/HdmiCecConfig.java +134 −0 Original line number Diff line number Diff line Loading @@ -22,12 +22,19 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.database.ContentObserver; import android.hardware.hdmi.HdmiControlManager; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -88,6 +95,23 @@ public class HdmiCecConfig { @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; private final ArrayMap<Setting, Set<SettingChangeListener>> mSettingChangeListeners = new ArrayMap<>(); private SettingsObserver mSettingsObserver; /** * Listener used to get notifications when value of a setting changes. */ public interface SettingChangeListener { /** * Called when value of a setting changes. * * @param setting name of a CEC setting that changed */ void onChange(@NonNull @CecSettingName String setting); } /** * Setting storage input/output helper class. */ Loading Loading @@ -159,6 +183,18 @@ public class HdmiCecConfig { } } private class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange, Uri uri) { String setting = uri.getLastPathSegment(); HdmiCecConfig.this.notifyGlobalSettingChanged(setting); } } @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, Loading Loading @@ -311,6 +347,7 @@ public class HdmiCecConfig { } else if (storage == STORAGE_SHARED_PREFS) { Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); notifySettingChanged(setting); } } Loading @@ -318,6 +355,103 @@ public class HdmiCecConfig { return Integer.decode(value.getIntValue()); } private void notifyGlobalSettingChanged(String setting) { switch (setting) { case Global.HDMI_CONTROL_ENABLED: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); break; case Global.HDMI_CEC_VERSION: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); break; case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); break; } } private void notifySettingChanged(@NonNull @CecSettingName String name) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } notifySettingChanged(setting); } private void notifySettingChanged(@NonNull Setting setting) { Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); if (listeners == null) { return; // No listeners registered, do nothing. } for (SettingChangeListener listener: listeners) { listener.onChange(setting.getName()); } } /** * This method registers Global Setting change observer. * Needs to be called once after initialization of HdmiCecConfig. */ public void registerGlobalSettingsObserver(Looper looper) { Handler handler = new Handler(looper); mSettingsObserver = new SettingsObserver(handler); ContentResolver resolver = mContext.getContentResolver(); String[] settings = new String[] { Global.HDMI_CONTROL_ENABLED, Global.HDMI_CEC_VERSION, Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, }; for (String setting: settings) { resolver.registerContentObserver(Global.getUriFor(setting), false, mSettingsObserver, UserHandle.USER_ALL); } } /** * This method unregisters Global Setting change observer. */ public void unregisterGlobalSettingsObserver() { ContentResolver resolver = mContext.getContentResolver(); resolver.unregisterContentObserver(mSettingsObserver); } /** * Register change listener for a given setting name. */ public void registerChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } @Storage int storage = getStorage(setting); if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) { throw new IllegalArgumentException("Change listeners for setting '" + name + "' not supported."); } if (!mSettingChangeListeners.containsKey(setting)) { mSettingChangeListeners.put(setting, new HashSet<>()); } mSettingChangeListeners.get(setting).add(listener); } /** * Remove change listener for a given setting name. */ public void removeChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); } if (mSettingChangeListeners.containsKey(setting)) { Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); listeners.remove(listener); if (listeners.isEmpty()) { mSettingChangeListeners.remove(setting); } } } /** * Returns a list of all settings based on the XML metadata. */ Loading
services/core/java/com/android/server/hdmi/HdmiControlService.java +1 −0 Original line number Diff line number Diff line Loading @@ -496,6 +496,7 @@ public class HdmiControlService extends SystemService { if (mMessageValidator == null) { mMessageValidator = new HdmiCecMessageValidator(this); } mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper); } private void bootCompleted() { Loading
services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +132 −0 Original line number Diff line number Diff line Loading @@ -18,13 +18,17 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.annotation.NonNull; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; Loading @@ -38,15 +42,23 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @SmallTest @Presubmit @RunWith(JUnit4.class) public final class HdmiCecConfigTest { private static final String TAG = "HdmiCecConfigTest"; private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; private final TestLooper mTestLooper = new TestLooper(); private Context mContext; @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; @Before public void setUp() throws Exception { Loading Loading @@ -1019,4 +1031,124 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); } @Test public void registerChangeListener_SharedPref_BasicSanity() { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"system_audio_mode_muting\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); verify(mSettingChangeListener).onChange( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); } @Test public void removeChangeListener_SharedPref_BasicSanity() { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"system_audio_mode_muting\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.removeChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); verify(mSettingChangeListener, never()).onChange( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); } /** * Externally modified Global Settings still need to be supported. This test verifies that * setting change notification is being forwarded to listeners registered via HdmiCecConfig. */ @Test public void globalSettingObserver_BasicSanity() throws Exception { CountDownLatch notifyLatch = new CountDownLatch(1); // Get current value of the setting in the system. String originalValue = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); try { HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( mContext, mStorageAdapter, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + "<cec-settings>" + " <setting name=\"hdmi_cec_enabled\"" + " value-type=\"int\"" + " user-configurable=\"true\">" + " <allowed-values>" + " <value int-value=\"0\" />" + " <value int-value=\"1\" />" + " </allowed-values>" + " <default-value int-value=\"1\" />" + " </setting>" + "</cec-settings>", null); hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper()); HdmiCecConfig.SettingChangeListener latchUpdateListener = new HdmiCecConfig.SettingChangeListener() { @Override public void onChange( @NonNull @HdmiControlManager.CecSettingName String setting) { notifyLatch.countDown(); assertThat(setting).isEqualTo( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); } }; hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, latchUpdateListener); mTestLooper.dispatchAll(); // Flip the value of the setting. String valueToSet = ((originalValue == null || originalValue.equals("1")) ? "0" : "1"); Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, valueToSet); assertThat(Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED)).isEqualTo(valueToSet); // Write Setting a 2nd time as the listener doesn't always trigger on the first write // in the test. Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, valueToSet); mTestLooper.dispatchAll(); if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) { fail("Timed out waiting for the notify callback"); } hdmiCecConfig.unregisterGlobalSettingsObserver(); } finally { // Restore the previous value of the setting in the system. Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, originalValue); } } }