CEC: make it sure to send vendor commands when changing a setting.

- Remove 'oneway' from IHdmiVendorCommandListener.
- Add flush() method to HdmiCecController.
- Use IoThread for HdmiCecController.

Bug: 18495592
Change-Id: I497f7b49e94dd4402058ecc89cb5b7a3d58bf1e1
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 0e8788a..1486fee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -129,7 +129,7 @@
     }
 
     private void init(long nativePtr) {
-        mIoHandler = new Handler(mService.getServiceLooper());
+        mIoHandler = new Handler(mService.getIoLooper());
         mControlHandler = new Handler(mService.getServiceLooper());
         mNativePtr = nativePtr;
     }
@@ -324,6 +324,7 @@
     @ServiceThreadOnly
     void setOption(int flag, int value) {
         assertRunOnServiceThread();
+        HdmiLogger.debug("setOption: [flag:%d, value:%d]", flag, value);
         nativeSetOption(mNativePtr, flag, value);
     }
 
@@ -501,6 +502,19 @@
         mControlHandler.post(runnable);
     }
 
+    @ServiceThreadOnly
+    void flush(final Runnable runnable) {
+        assertRunOnServiceThread();
+        runOnIoThread(new Runnable() {
+            @Override
+            public void run() {
+                // This ensures the runnable for cleanup is performed after all the pending
+                // commands are processed by IO thread.
+                runOnServiceThread(runnable);
+            }
+        });
+    }
+
     private boolean isAcceptableAddress(int address) {
         // Can access command targeting devices available in local device or broadcast command.
         if (address == Constants.ADDR_BROADCAST) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8a25f62..5f8b389 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2021,30 +2021,52 @@
     void setControlEnabled(boolean enabled) {
         assertRunOnServiceThread();
 
-        if (!enabled) {
-            // Call the vendor handler before the service is disabled.
-            invokeVendorCommandListenersOnControlStateChanged(false,
-                    HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
-        }
-        int value = toInt(enabled);
-        mCecController.setOption(OPTION_CEC_ENABLE, value);
-        mMhlController.setOption(OPTION_MHL_ENABLE, value);
-
         synchronized (mLock) {
             mHdmiControlEnabled = enabled;
         }
 
         if (enabled) {
-            initializeCec(INITIATED_BY_ENABLE_CEC);
-        } else {
-            disableDevices(new PendingActionClearedCallback() {
-                @Override
-                public void onCleared(HdmiCecLocalDevice device) {
-                    assertRunOnServiceThread();
-                    clearLocalDevices();
-                }
-            });
+            enableHdmiControlService();
+            return;
         }
+        // Call the vendor handler before the service is disabled.
+        invokeVendorCommandListenersOnControlStateChanged(false,
+                HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);
+        // Post the remained tasks in the service thread again to give the vendor-issued-tasks
+        // a chance to run.
+        runOnServiceThread(new Runnable() {
+            @Override
+            public void run() {
+                disableHdmiControlService();
+            }
+        });
+        return;
+    }
+
+    @ServiceThreadOnly
+    private void enableHdmiControlService() {
+        mCecController.setOption(OPTION_CEC_ENABLE, ENABLED);
+        mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
+
+        initializeCec(INITIATED_BY_ENABLE_CEC);
+    }
+
+    @ServiceThreadOnly
+    private void disableHdmiControlService() {
+        disableDevices(new PendingActionClearedCallback() {
+            @Override
+            public void onCleared(HdmiCecLocalDevice device) {
+                assertRunOnServiceThread();
+                mCecController.flush(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCecController.setOption(OPTION_CEC_ENABLE, DISABLED);
+                        mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
+                        clearLocalDevices();
+                    }
+                });
+            }
+        });
     }
 
     @ServiceThreadOnly