Merge "HDMI: Source asserts <AS> if TV doesn't answer to <Request AS>" into main
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d0ad6fc..b696c54 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -444,14 +444,62 @@
                         new Runnable() {
                             @Override
                             public void run() {
-                                if (!isActiveSource()) {
+                                if (isActiveSource()) {
+                                    return;
+                                }
+
+                                if (getActiveSource().logicalAddress != Constants.ADDR_TV) {
                                     startHdmiCecActiveSourceLostActivity();
                                     mDelayedStandbyOnActiveSourceLostHandler
                                             .removeCallbacksAndMessages(null);
                                     mDelayedStandbyOnActiveSourceLostHandler.postDelayed(
                                             new DelayedStandbyOnActiveSourceLostRunnable(),
                                             STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+                                    return;
                                 }
+
+                                // We observed specific TV panels (old models) that send faulty CEC
+                                // source changing messages, especially during wake-up.
+                                // This request helps to check if the TV correctly asserted active
+                                // source or not. If the request times out, active source is
+                                // asserted by the local device.
+                                addAndStartAction(new RequestActiveSourceAction(mService.playback(),
+                                        new IHdmiControlCallback.Stub() {
+                                    @Override
+                                    public void onComplete(int result) {
+                                        // If a device answers to <Request Active Source>, the
+                                        // pop-up should be triggered.
+                                        // During this action, the TV can switch to an HDMI input
+                                        // with a non-CEC capable device that won't be able to
+                                        // answer this request.
+                                        // In this case, the known active source would be
+                                        // represented by a valid physical address, but invalid
+                                        // logical address. The pop-up will be shown and the local
+                                        // device will not assert active source.
+                                        if (result == HdmiControlManager.RESULT_SUCCESS
+                                                || getActiveSource().logicalAddress
+                                                != Constants.ADDR_TV) {
+                                                startHdmiCecActiveSourceLostActivity();
+                                                mDelayedStandbyOnActiveSourceLostHandler
+                                                        .removeCallbacksAndMessages(null);
+                                                mDelayedStandbyOnActiveSourceLostHandler
+                                                        .postDelayed(
+                                                                new DelayedStandbyOnActiveSourceLostRunnable(),
+                                                        STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+                                        } else {
+                                            // The request times out and the local device is not
+                                            // active source, but the TV previously asserted active
+                                            // source.
+                                            if (getActiveSource().logicalAddress
+                                                    == Constants.ADDR_TV) {
+                                                mService.setAndBroadcastActiveSource(
+                                                        mService.getPhysicalAddress(),
+                                                        getDeviceInfo().getDeviceType(),
+                                                        Constants.ADDR_BROADCAST,
+                                                        "RequestActiveSourceAction#RESULT_TIMEOUT");
+                                            }
+                                        }
+                                    }}));
                             }
                         }, POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
                 return;
@@ -698,6 +746,7 @@
         removeAction(HotplugDetectionAction.class);
         removeAction(NewDeviceAction.class);
         removeAction(PowerStatusMonitorActionFromPlayback.class);
+        removeAction(RequestActiveSourceAction.class);
         super.disableDevice(initiatedByCec, callback);
         clearDeviceInfoList();
         checkIfPendingActionsCleared();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 81be0ba..cb0b4b0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4344,6 +4344,7 @@
         if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
             HdmiCecLocalDevicePlayback playback = playback();
             playback.dismissUiOnActiveSourceStatusRecovered();
+            playback.removeAction(RequestActiveSourceAction.class);
             playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
                     caller);
             playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index a33d70a..b0e9398 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -22,11 +22,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
- * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
- * panels.
- * This action has a delay before sending <Request Active Source>. This is because it should wait
- * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
- * received or the TV switched to another input.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ *
+ * For TV panels, this action has a delay before sending <Request Active Source>. This is because it
+ * should wait for a possible request from LauncherX and can be cancelled if an <Active Source>
+ * message was received or the TV switched to another input.
  */
 public class RequestActiveSourceAction extends HdmiCecFeatureAction {
     private static final String TAG = "RequestActiveSourceAction";
@@ -55,6 +55,13 @@
     boolean start() {
         Slog.v(TAG, "RequestActiveSourceAction started.");
 
+        if (localDevice().mService.isPlaybackDevice()) {
+            mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+            sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+            addTimer(mState, HdmiConfig.TIMEOUT_MS);
+            return true;
+        }
+
         mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
 
         // We wait for default timeout to allow the message triggered by the LauncherX API call to
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index a0005d9..3360e1d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -413,7 +413,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -422,9 +423,8 @@
         int newPlaybackPhysicalAddress = 0x2100;
         int switchPhysicalAddress = 0x2000;
         mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
-        mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
+        mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -457,7 +457,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -617,7 +618,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -722,7 +724,8 @@
         assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
                 ADDR_INVALID);
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS,true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -1265,14 +1268,35 @@
         assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
-
-        // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        // After 30s of device inactivity, device would assert active source.
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
     }
 
     @Test
+    public void handleActiveSourceFromTv_tvNotAnswerRequest_assertActiveSource() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+        mPowerManager.setInteractive(true);
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+                .isEqualTo(Constants.HANDLED);
+        message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+        // After 30s of device inactivity, device would assert active source.
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                false);
+        assertThat(mPowerManager.isInteractive()).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+    }
+
+    @Test
     public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() {
         mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
                 mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
@@ -1343,7 +1367,8 @@
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
         mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
         // 3. DUT becomes <AS> again.
@@ -1704,9 +1729,9 @@
         assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
                 .isEqualTo(Constants.HANDLED);
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+                false);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
@@ -2323,11 +2348,197 @@
         mTestLooper.dispatchAll();
 
         // After 30s of device inactivity, device would go to sleep.
-        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+                true);
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
     @Test
+    public void onActiveSourceLostToTv_requestActiveSourceAnsweredFromTv_showPopup() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mNativeWrapper.onCecMessage(activeSourceFromTv);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+    }
+
+    @Test
+    public void onActiveSourceLostToTv_requestActiveSourceNotAnswered_assertActiveSource() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is not shown, playback device asserts active source since TV doesn't answer the
+        // request.
+        assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+        assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(mPlaybackLogicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(mPlaybackPhysicalAddress);
+    }
+
+    @Test
+    public void onActiveSourceLost_requestActiveSourceNotAnswered_playbackIsAS_dontShowPopup() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage setStreamPathToPlayback = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                mPlaybackPhysicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is not shown since playback device is active source.
+        assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+        assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(mPlaybackLogicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(mPlaybackPhysicalAddress);
+    }
+
+    @Test
+    public void onActiveSourceLost_requestASNotAnswered_setStreamPathToNonCecInput_dontShowPopup() {
+        int otherPhysicalAddress = mPlaybackPhysicalAddress + 0x0100;
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mPowerManager.setInteractive(true);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSourceFromTv =
+                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage requestActiveSource =
+                HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+        HdmiCecMessage setStreamPathToOtherInput = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+                otherPhysicalAddress);
+        HdmiCecMessage activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        // RequestActiveSourceAction is triggered.
+        assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToOtherInput))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        mTestLooper.moveTimeForward(TIMEOUT_MS);
+        mTestLooper.dispatchAll();
+
+        // Pop-up is shown, playback device doesn't assert active source since active path is
+        // switched to a non-CEC device.
+        assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(ADDR_INVALID);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(otherPhysicalAddress);
+    }
+
+    @Test
     public void onActiveSourceLost_interactionWithDut_noStandbyAfterTimeout() {
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -2350,7 +2561,7 @@
         mTestLooper.dispatchAll();
 
         // User interacted with the DUT, so the device will not go to standby.
-        skipActiveSourceLostUi(0);
+        skipActiveSourceLostUi(0, true, true);
         assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
         assertThat(mPowerManager.isInteractive()).isTrue();
         assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
@@ -2387,6 +2598,10 @@
         // Pop-up is triggered.
         mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
+        // RequestActiveSourceAction is triggered and TV confirms active source.
+        mNativeWrapper.onCecMessage(activeSourceFromTv);
+        mTestLooper.dispatchAll();
+
         assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
 
         assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
@@ -2407,6 +2622,8 @@
 
     @Test
     public void onActiveSourceLost_incomingRoutingChangeToDut_noStandbyAfterTimeout() {
+        int otherPlaybackLogicalAddress = mPlaybackLogicalAddress == Constants.ADDR_PLAYBACK_2
+                ? Constants.ADDR_PLAYBACK_1 : Constants.ADDR_PLAYBACK_2;
         mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
                 HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                 HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -2420,18 +2637,21 @@
         mNativeWrapper.clearResultMessages();
         mTestLooper.dispatchAll();
 
-        HdmiCecMessage activeSourceFromTv =
-                HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+        HdmiCecMessage activeSourceFromOtherPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(otherPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress + 0x0100);
         HdmiCecMessage activeSourceFromPlayback =
                 HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
                         mPlaybackPhysicalAddress);
         HdmiCecMessage routingChangeToPlayback =
-                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+                HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV,
+                        mPlaybackPhysicalAddress + 0x0100,
                         mPlaybackPhysicalAddress);
 
-        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromOtherPlayback))
                 .isEqualTo(Constants.HANDLED);
-        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
+                otherPlaybackLogicalAddress);
         mTestLooper.dispatchAll();
 
         // Pop-up is triggered.
@@ -2600,13 +2820,30 @@
         assertThat(mPowerManager.isInteractive()).isFalse();
     }
 
-    private void skipActiveSourceLostUi(long idleDuration) {
+    private void skipActiveSourceLostUi(long idleDuration, boolean activeSourceLostToTv,
+            boolean tvAnswersRequest) {
         mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
+        if (activeSourceLostToTv) {
+            // RequestActiveSourceAction is triggered.
+            mTestLooper.moveTimeForward(TIMEOUT_MS);
+            mTestLooper.dispatchAll();
+            if (tvAnswersRequest) {
+                HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV,
+                        0x0000);
+                mNativeWrapper.onCecMessage(activeSource);
+                mTestLooper.dispatchAll();
+            } else {
+                mTestLooper.moveTimeForward(TIMEOUT_MS);
+                mTestLooper.dispatchAll();
+                assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+                return;
+            }
+        }
         assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
 
         mPowerManagerInternal.setIdleDuration(idleDuration);
         mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
         mTestLooper.dispatchAll();
     }
-}
+}
\ No newline at end of file