CEC: Revisit the behavior of HDMI_SYSTEM_AUDIO_ENABLED

Currently the system setting, HDMI_SYSTEM_AUDIO_ENABLED, is used to
store the latest system audio mode status so that TV can keep this
status over reboot. But because the name is a little confusing and the
behavior isn't intuitive, it is likely to use this in a wrong way.
This change renames this setting to HDMI_SYSTEM_AUDIO_CONTROL_ENABLED
and tweak the purpose of it. Now, it will act more like a switch for
System Audio Control feature, so user can disable or enable this feature
entirely. With this way, implementation of audio output option will
also become easier.

Bug: 31449672
Test: Tested on archer
Change-Id: Ice8717135272d4b86665a3452bfe7527c0d6c08b
(cherry picked from commit 7b7aa8fb31ccf0cd3f36162a52f080263dd89e77)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 52aa1d5..be4802c 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7654,12 +7654,14 @@
        public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
 
        /**
-        * Whether HDMI system audio is enabled. If enabled, TV internal speaker is muted,
-        * and the output is redirected to AV Receiver connected via
-        * {@Global#HDMI_SYSTEM_AUDIO_OUTPUT}.
+        * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
+        * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
+        * be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be
+        * activated.
         * @hide
         */
-       public static final String HDMI_SYSTEM_AUDIO_ENABLED = "hdmi_system_audio_enabled";
+        public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED =
+                "hdmi_system_audio_control_enabled";
 
        /**
         * Whether TV will automatically turn on upon reception of the CEC command
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 7eb0582..98c9e78 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -97,7 +97,7 @@
     SettingProto download_max_bytes_over_mobile = 52;
     SettingProto download_recommended_max_bytes_over_mobile = 53;
     SettingProto hdmi_control_enabled = 54;
-    SettingProto hdmi_system_audio_enabled = 55;
+    SettingProto hdmi_system_audio_control_enabled = 55;
     SettingProto hdmi_control_auto_wakeup_enabled = 56;
     SettingProto hdmi_control_auto_device_off_enabled = 57;
     SettingProto mhl_input_switching_enabled = 58;
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 903ef84..0cfdaf5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -198,7 +198,7 @@
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
-                    Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
+                    Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                     Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
                     Settings.Global.HTTP_PROXY,
                     Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1fe3c48..4a54c0e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -240,8 +240,8 @@
                 Settings.Global.HDMI_CONTROL_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
-                GlobalSettingsProto.HDMI_SYSTEM_AUDIO_ENABLED);
+                Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
+                GlobalSettingsProto.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED);
         dumpSetting(s, p,
                 Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_AUTO_WAKEUP_ENABLED);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index d81e092..a50ec49f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -72,7 +72,8 @@
     @ServiceThreadOnly
     private boolean mArcEstablished = false;
 
-    // Stores whether ARC feature is enabled per port. True by default for all the ARC-enabled ports.
+    // Stores whether ARC feature is enabled per port.
+    // True by default for all the ARC-enabled ports.
     private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
 
     // Whether System audio mode is activated or not.
@@ -80,6 +81,10 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioActivated = false;
 
+    // Whether the System Audio Control feature is enabled or not. True by default.
+    @GuardedBy("mLock")
+    private boolean mSystemAudioControlFeatureEnabled;
+
     // The previous port id (input) before switching to the new one. This is remembered in order to
     // be able to switch to it upon receiving <Inactive Source> from currently active source.
     // This remains valid only when the active source was switched via one touch play operation
@@ -186,6 +191,8 @@
         mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 true);
         mAutoWakeup = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+        mSystemAudioControlFeatureEnabled =
+                mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
         mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
     }
 
@@ -778,14 +785,11 @@
                         addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                         addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
 
-                        // If there is AVR, initiate System Audio Auto initiation action,
-                        // which turns on and off system audio according to last system
-                        // audio setting.
                         HdmiDeviceInfo avr = getAvrDeviceInfo();
                         if (avr != null) {
                             onNewAvrAdded(avr);
                         } else {
-                            setSystemAudioMode(false, true);
+                            setSystemAudioMode(false);
                         }
                     }
                 });
@@ -818,13 +822,13 @@
     void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (!mService.isControlEnabled() || hasAction(DeviceDiscoveryAction.class)) {
-            setSystemAudioMode(false, true);
+            setSystemAudioMode(false);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr == null) {
-            setSystemAudioMode(false, true);
+            setSystemAudioMode(false);
             invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
             return;
         }
@@ -834,12 +838,13 @@
     }
 
     // # Seq 25
-    void setSystemAudioMode(boolean on, boolean updateSetting) {
-        HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
-
-        if (updateSetting) {
-            mService.writeBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, on);
+    void setSystemAudioMode(boolean on) {
+        if (!isSystemAudioControlFeatureEnabled() && on) {
+            HdmiLogger.debug("Cannot turn on system audio mode "
+                    + "because the System Audio Control feature is disabled.");
+            return;
         }
+        HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
         updateAudioManagerForSystemAudio(on);
         synchronized (mLock) {
             if (mSystemAudioActivated != on) {
@@ -863,8 +868,21 @@
         }
     }
 
-    boolean getSystemAudioModeSetting() {
-        return mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, false);
+    @ServiceThreadOnly
+    void setSystemAudioControlFeatureEnabled(boolean enabled) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            mSystemAudioControlFeatureEnabled = enabled;
+        }
+        if (hasSystemAudioDevice()) {
+            changeSystemAudioMode(enabled, null);
+        }
+    }
+
+    boolean isSystemAudioControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mSystemAudioControlFeatureEnabled;
+        }
     }
 
     /**
@@ -1112,6 +1130,7 @@
     @ServiceThreadOnly
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        boolean systemAudioStatus = HdmiUtils.parseCommandParamSystemAudioStatus(message);
         if (!isMessageForSystemAudio(message)) {
             if (getAvrDeviceInfo() == null) {
                 // AVR may not have been discovered yet. Delay the message processing.
@@ -1121,10 +1140,15 @@
                 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             }
             return true;
+        } else if (systemAudioStatus && !isSystemAudioControlFeatureEnabled()) {
+            HdmiLogger.debug("Ignoring <Set System Audio Mode> message "
+                    + "because the System Audio Control feature is disabled: %s", message);
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
         }
         removeAction(SystemAudioAutoInitiationAction.class);
         SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
-                message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
+                message.getSource(), systemAudioStatus, null);
         addAndStartAction(action);
         return true;
     }
@@ -1138,7 +1162,7 @@
             // Ignore this message.
             return true;
         }
-        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
+        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
         return true;
     }
 
@@ -1882,6 +1906,7 @@
         pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
         pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
         pw.println("mSystemAudioMute: " + mSystemAudioMute);
+        pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
         pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
         pw.println("mAutoWakeup: " + mAutoWakeup);
         pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 89b10ac..6864e1e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -486,7 +486,7 @@
                 Global.HDMI_CONTROL_ENABLED,
                 Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                 Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
-                Global.HDMI_SYSTEM_AUDIO_ENABLED,
+                Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
                 Global.MHL_POWER_CHARGE_ENABLED
         };
@@ -525,9 +525,9 @@
                     }
                     // No need to propagate to HAL.
                     break;
-                case Global.HDMI_SYSTEM_AUDIO_ENABLED:
-                    if (isTvDeviceEnabled() && tv().isSystemAudioActivated() != enabled) {
-                        tv().changeSystemAudioMode(enabled, null);
+                case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
+                    if (isTvDeviceEnabled()) {
+                        tv().setSystemAudioControlFeatureEnabled(enabled);
                     }
                     break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index e1bcd99..7670dcc 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -262,8 +262,7 @@
             return;
         }
 
-        // Turn off system audio mode and update settings.
-        tv().setSystemAudioMode(false, true);
+        tv().setSystemAudioMode(false);
         if (tv().isArcEstablished()) {
             tv().enableAudioReturnChannel(false);
             addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index af1a85d..449b208 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -133,7 +133,7 @@
     }
 
     protected void setSystemAudioMode(boolean mode) {
-        tv().setSystemAudioMode(mode, true);
+        tv().setSystemAudioMode(mode);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index 01063b7..d347a91 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -50,7 +50,7 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != SendMessageResult.SUCCESS) {
-                    tv().setSystemAudioMode(false, true);
+                    tv().setSystemAudioMode(false);
                     finish();
                 }
             }
@@ -71,18 +71,24 @@
         return false;
     }
 
-    private void handleSystemAudioModeStatusMessage(boolean isSystemAudioModeOn) {
+    private void handleSystemAudioModeStatusMessage(boolean currentSystemAudioMode) {
         if (!canChangeSystemAudio()) {
             HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
             finish();
             return;
         }
 
-        boolean systemAudioModeSetting = tv().getSystemAudioModeSetting();
-        if (systemAudioModeSetting && !isSystemAudioModeOn) {
-            addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, systemAudioModeSetting, null));
+        // If System Audio Control feature is enabled, turn on system audio mode when new AVR is
+        // detected. Otherwise, turn off system audio mode.
+        boolean targetSystemAudioMode = tv().isSystemAudioControlFeatureEnabled();
+        if (currentSystemAudioMode != targetSystemAudioMode) {
+            // Start System Audio Control feature actions only if necessary.
+            addAndStartAction(
+                    new SystemAudioActionFromTv(tv(), mAvrAddress, targetSystemAudioMode, null));
         } else {
-            tv().setSystemAudioMode(isSystemAudioModeOn, true);
+            // If AVR already has correct system audio mode, update target system audio mode
+            // immediately rather than starting feature action.
+            tv().setSystemAudioMode(targetSystemAudioMode);
         }
         finish();
     }
@@ -101,13 +107,15 @@
     }
 
     private void handleSystemAudioModeStatusTimeout() {
-        if (tv().getSystemAudioModeSetting()) {
-            if (canChangeSystemAudio()) {
-                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null));
-            }
-        } else {
-            tv().setSystemAudioMode(false, true);
+        if (!canChangeSystemAudio()) {
+            HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
+            finish();
+            return;
         }
+        // If we can't get the current system audio mode status, just try to turn on/off system
+        // audio mode according to the system audio control setting.
+        addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress,
+                tv().isSystemAudioControlFeatureEnabled(), null));
         finish();
     }