CEC: Content observer to handle TV Setting update

Replaced the API setOption with content observer to read/get informed of
the CEC settings.

Bug: 16855247

Change-Id: Ieff2399bbfe83f05af4448e7337f91ef40d1e24d
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index a9040cf..a3f6c00 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -35,55 +35,6 @@
 public final class HdmiTvClient extends HdmiClient {
     private static final String TAG = "HdmiTvClient";
 
-    // Definitions used for setOption(). These should be in sync with the definition
-    // in hardware/libhardware/include/hardware/{hdmi_cec.h,mhl.h}.
-
-    /**
-     * TV gets turned on by incoming <Text/Image View On>. {@code ENABLED} by default.
-     * If set to {@code DISABLED}, TV won't turn on automatically.
-     */
-    public static final int OPTION_CEC_AUTO_WAKEUP = 1;
-
-    /**
-     * If set to {@code DISABLED}, all CEC commands are discarded.
-     *
-     * <p> This option is for internal use only, not supposed to be used by other components.
-     * @hide
-     */
-    public static final int OPTION_CEC_ENABLE = 2;
-
-    /**
-     * If set to {@code DISABLED}, system service yields control of CEC to sub-microcontroller.
-     * If {@code ENABLED}, it take the control back.
-     *
-     * <p> This option is for internal use only, not supposed to be used by other components.
-     * @hide
-     */
-    public static final int OPTION_CEC_SERVICE_CONTROL = 3;
-
-    /**
-     * Put other devices to standby when TV goes to standby. {@code ENABLED} by default.
-     * If set to {@code DISABLED}, TV doesn't send &lt;Standby&gt; to other devices.
-     */
-    public static final int OPTION_CEC_AUTO_DEVICE_OFF = 4;
-
-    /** If set to {@code DISABLED}, TV does not switch ports when mobile device is connected. */
-    public static final int OPTION_MHL_INPUT_SWITCHING = 101;
-
-    /** If set to {@code ENABLED}, TV disables power charging for mobile device. */
-    public static final int OPTION_MHL_POWER_CHARGE = 102;
-
-    /**
-     * If set to {@code DISABLED}, all MHL commands are discarded.
-     *
-     * <p> This option is for internal use only, not supposed to be used by other components.
-     * @hide
-     */
-    public static final int OPTION_MHL_ENABLE = 103;
-
-    public static final int DISABLED = 0;
-    public static final int ENABLED = 1;
-
     HdmiTvClient(IHdmiControlService service) {
         super(service);
     }
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index d6cb492..5422bc2 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -52,9 +52,7 @@
     void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
     void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
     void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
-    void setControlEnabled(boolean enabled);
     void setArcMode(boolean enabled);
-    void setOption(int option, int value);
     void setProhibitMode(boolean enabled);
     void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
     void setSystemAudioMute(boolean mute);
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 0b57474..0003210 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -214,5 +214,35 @@
     static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3;
     static final int RECORDING_TYPE_OWN_SOURCE = 4;
 
+    // Definitions used for setOption(). These should be in sync with the definition
+    // in hardware/libhardware/include/hardware/{hdmi_cec.h,mhl.h}.
+
+    // TV gets turned on by incoming <Text/Image View On>. enabled by default.
+    // If set to disabled, TV won't turn on automatically.
+    static final int OPTION_CEC_AUTO_WAKEUP = 1;
+
+    // If set to disabled, all CEC commands are discarded.
+    static final int OPTION_CEC_ENABLE = 2;
+
+    // If set to disabled, system service yields control of CEC to sub-microcontroller.
+    // If enabled, it take the control back.
+    static final int OPTION_CEC_SERVICE_CONTROL = 3;
+
+    // Put other devices to standby when TV goes to standby. enabled by default.
+    // If set to disabled, TV doesn't send <Standby> to other devices.
+    static final int OPTION_CEC_AUTO_DEVICE_OFF = 4;
+
+    // If set to disabled, TV does not switch ports when mobile device is connected.
+    static final int OPTION_MHL_INPUT_SWITCHING = 101;
+
+    // If set to enabled, TV disables power charging for mobile device.
+    static final int OPTION_MHL_POWER_CHARGE = 102;
+
+    // If set to disabled, all MHL commands are discarded.
+    static final int OPTION_MHL_ENABLE = 103;
+
+    static final int DISABLED = 0;
+    static final int ENABLED = 1;
+
     private Constants() { /* cannot be instantiated */ }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index ad5b2ba..183f299 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -30,8 +30,8 @@
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
 
 import android.content.Intent;
-import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiRecordSources;
 import android.hardware.hdmi.HdmiTimerRecordSources;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -1274,14 +1274,12 @@
     void setAutoDeviceOff(boolean enabled) {
         assertRunOnServiceThread();
         mAutoDeviceOff = enabled;
-        mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, enabled);
     }
 
     @ServiceThreadOnly
     void setAutoWakeup(boolean enabled) {
         assertRunOnServiceThread();
         mAutoWakeup = enabled;
-        mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, enabled);
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 81b99f0..6f68bd8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -16,17 +16,27 @@
 
 package com.android.server.hdmi;
 
+import static com.android.server.hdmi.Constants.DISABLED;
+import static com.android.server.hdmi.Constants.ENABLED;
+import static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_DEVICE_OFF;
+import static com.android.server.hdmi.Constants.OPTION_CEC_AUTO_WAKEUP;
+import static com.android.server.hdmi.Constants.OPTION_CEC_ENABLE;
+import static com.android.server.hdmi.Constants.OPTION_CEC_SERVICE_CONTROL;
+import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
+import static com.android.server.hdmi.Constants.OPTION_MHL_INPUT_SWITCHING;
+import static com.android.server.hdmi.Constants.OPTION_MHL_POWER_CHARGE;
+
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.database.ContentObserver;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiHotplugEvent;
 import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.hdmi.HdmiTvClient;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.hdmi.IHdmiDeviceEventListener;
@@ -36,6 +46,7 @@
 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
 import android.hardware.hdmi.IHdmiVendorCommandListener;
 import android.media.AudioManager;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -44,6 +55,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -196,6 +208,8 @@
     // Handler used to run a task in service thread.
     private final Handler mHandler = new Handler();
 
+    private final SettingsObserver mSettingsObserver;
+
     @Nullable
     private HdmiCecController mCecController;
 
@@ -229,6 +243,7 @@
         super(context);
         mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
                 com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
+        mSettingsObserver = new SettingsObserver(mHandler);
     }
 
     @Override
@@ -241,8 +256,7 @@
         mCecController = HdmiCecController.create(this);
         if (mCecController != null) {
             // TODO: Remove this as soon as OEM's HAL implementation is corrected.
-            mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE,
-                    HdmiTvClient.ENABLED);
+            mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
 
             // TODO: load value for mHdmiControlEnabled from preference.
             if (mHdmiControlEnabled) {
@@ -279,24 +293,78 @@
         mWakeUpMessageReceived = false;
 
         if (isTvDevice()) {
-            mCecController.setOption(HdmiTvClient.OPTION_CEC_AUTO_WAKEUP,
-                    tv().getAutoWakeup() ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED);
+            mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup()));
+            registerContentObserver();
         }
     }
 
+
+    private void registerContentObserver() {
+        ContentResolver resolver = getContext().getContentResolver();
+        String[] settings = new String[] {
+                Global.HDMI_CONTROL_ENABLED,
+                Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
+                Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                Global.MHL_INPUT_SWITCHING_ENABLED,
+                Global.MHL_POWER_CHARGE_ENABLED
+        };
+        for (String s: settings) {
+            resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver,
+                    UserHandle.USER_ALL);
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        public SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            String option = uri.getLastPathSegment();
+            boolean enabled = readBooleanSetting(option, true);
+            switch (option) {
+                case Global.HDMI_CONTROL_ENABLED:
+                    setControlEnabled(enabled);
+                    break;
+                case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
+                    tv().setAutoWakeup(enabled);
+                    setOption(OPTION_CEC_AUTO_WAKEUP, toInt(enabled));
+                    break;
+                case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
+                    tv().setAutoDeviceOff(enabled);
+                    // No need to propagate to HAL.
+                    break;
+                case Global.MHL_INPUT_SWITCHING_ENABLED:
+                    setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
+                    break;
+                case Global.MHL_POWER_CHARGE_ENABLED:
+                    setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled));
+                    break;
+            }
+        }
+    }
+
+    private static int toInt(boolean enabled) {
+        return enabled ? ENABLED : DISABLED;
+    }
+
     boolean readBooleanSetting(String key, boolean defVal) {
         ContentResolver cr = getContext().getContentResolver();
-        return Global.getInt(cr, key, defVal ? Constants.TRUE : Constants.FALSE) == Constants.TRUE;
+        return Global.getInt(cr, key, toInt(defVal)) == ENABLED;
     }
 
     void writeBooleanSetting(String key, boolean value) {
         ContentResolver cr = getContext().getContentResolver();
-        Global.putInt(cr, key, value ? Constants.TRUE : Constants.FALSE);
+        Global.putInt(cr, key, toInt(value));
+    }
+
+    private void unregisterSettingsObserver() {
+        getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
     }
 
     private void initializeCec(int initiatedBy) {
-        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL,
-                HdmiTvClient.ENABLED);
+        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED);
         initializeLocalDevices(mLocalDevices, initiatedBy);
     }
 
@@ -965,18 +1033,6 @@
         }
 
         @Override
-        public void setControlEnabled(final boolean enabled) {
-            enforceAccessPermission();
-            runOnServiceThread(new Runnable() {
-                @Override
-                public void run() {
-                    handleHdmiControlStatusChanged(enabled);
-
-                }
-            });
-        }
-
-        @Override
         public void setSystemAudioVolume(final int oldIndex, final int newIndex,
                 final int maxIndex) {
             enforceAccessPermission();
@@ -1025,30 +1081,6 @@
         }
 
         @Override
-        public void setOption(final int key, final int value) {
-            enforceAccessPermission();
-            if (!isTvDevice()) {
-                return;
-            }
-            switch (key) {
-                case HdmiTvClient.OPTION_CEC_AUTO_WAKEUP:
-                    tv().setAutoWakeup(value == HdmiTvClient.ENABLED);
-                    mCecController.setOption(key, value);
-                    break;
-                case HdmiTvClient.OPTION_CEC_AUTO_DEVICE_OFF:
-                    // No need to pass this option to HAL.
-                    tv().setAutoDeviceOff(value == HdmiTvClient.ENABLED);
-                    break;
-                case HdmiTvClient.OPTION_MHL_INPUT_SWITCHING:  // Fall through
-                case HdmiTvClient.OPTION_MHL_POWER_CHARGE:
-                    if (mMhlController != null) {
-                        mMhlController.setOption(key, value);
-                    }
-                    break;
-            }
-        }
-
-        @Override
         public void setProhibitMode(final boolean enabled) {
             enforceAccessPermission();
             if (!isTvDevice()) {
@@ -1502,6 +1534,9 @@
         for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
             device.disableDevice(mStandbyMessageReceived, callback);
         }
+        if (isTvDevice()) {
+            unregisterSettingsObserver();
+        }
     }
 
     @ServiceThreadOnly
@@ -1527,7 +1562,7 @@
             device.onStandby(mStandbyMessageReceived);
         }
         mStandbyMessageReceived = false;
-        mCecController.setOption(HdmiTvClient.OPTION_CEC_SERVICE_CONTROL, HdmiTvClient.DISABLED);
+        mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, DISABLED);
     }
 
     private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
@@ -1572,13 +1607,19 @@
     }
 
     @ServiceThreadOnly
-    private void handleHdmiControlStatusChanged(boolean enabled) {
+    void setOption(int key, int value) {
+        assertRunOnServiceThread();
+        mCecController.setOption(key, value);
+    }
+
+    @ServiceThreadOnly
+    void setControlEnabled(boolean enabled) {
         assertRunOnServiceThread();
 
-        int value = enabled ? HdmiTvClient.ENABLED : HdmiTvClient.DISABLED;
-        mCecController.setOption(HdmiTvClient.OPTION_CEC_ENABLE, value);
+        int value = toInt(enabled);
+        mCecController.setOption(OPTION_CEC_ENABLE, value);
         if (mMhlController != null) {
-            mMhlController.setOption(HdmiTvClient.OPTION_MHL_ENABLE, value);
+            mMhlController.setOption(OPTION_MHL_ENABLE, value);
         }
 
         synchronized (mLock) {