Merge "DO NOT MERGE CEC: Queue actions for starting later when not ready" into lmp-dev
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index b2300a6..fc53c50 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -73,8 +73,9 @@
     }
 
     /**
-     * Called right after the action is created. Initialization or first step to take
-     * for the action can be done in this method.
+     * Called after the action is created. Initialization or first step to take
+     * for the action can be done in this method. Shall update {@code mState} to
+     * indicate that the action has started.
      *
      * @return true if the operation is successful; otherwise false.
      */
@@ -162,6 +163,10 @@
         mActionTimer.sendTimerMessage(state, delayMillis);
     }
 
+    boolean started() {
+        return mState != STATE_NONE;
+    }
+
     protected final void sendCommand(HdmiCecMessage cmd) {
         mService.sendCecCommand(cmd);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 41ac589..8f9af61 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -617,14 +617,25 @@
     @ServiceThreadOnly
     void addAndStartAction(final HdmiCecFeatureAction action) {
         assertRunOnServiceThread();
+        mActions.add(action);
         if (mService.isPowerStandbyOrTransient()) {
-            Slog.w(TAG, "Skip the action during Standby: " + action);
+            Slog.i(TAG, "Not ready to start action. Queued for deferred start:" + action);
             return;
         }
-        mActions.add(action);
         action.start();
     }
 
+    @ServiceThreadOnly
+    void startQueuedActions() {
+        assertRunOnServiceThread();
+        for (HdmiCecFeatureAction action : mActions) {
+            if (!action.started()) {
+                Slog.i(TAG, "Starting queued action:" + action);
+                action.start();
+            }
+        }
+    }
+
     // See if we have an action of a given type in progress.
     @ServiceThreadOnly
     <T extends HdmiCecFeatureAction> boolean hasAction(final Class<T> clazz) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index b0ddf2ac..780d54b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -39,11 +39,18 @@
     }
 
     @Override
+    void init() {
+        super.init();
+        mIsActiveSource = false;
+    }
+
+    @Override
     @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mService.getPhysicalAddress(), mDeviceType));
+        startQueuedActions();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index bd9f04b..0fb4b48 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -142,6 +142,7 @@
         launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
                 reason != HdmiControlService.INITIATED_BY_BOOT_UP);
         launchDeviceDiscovery();
+        startQueuedActions();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 4d9b4e9..60d1520 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -415,12 +415,17 @@
         assertRunOnServiceThread();
         // A container for [Device type, Local device info].
         ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
-        clearLocalDevices();
         for (int type : mLocalDevices) {
-            final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
+            HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
+            if (localDevice == null) {
+                localDevice = HdmiCecLocalDevice.create(this, type);
+            }
             localDevice.init();
             localDevices.add(localDevice);
         }
+        // It's now safe to flush existing local devices from mCecController since they were
+        // already moved to 'localDevices'.
+        clearLocalDevices();
         allocateLogicalAddress(localDevices, initiatedBy);
     }