From 888ee5bea04a389007e56b02696fd52c74fffabf Mon Sep 17 00:00:00 2001 From: Yan Han Date: Thu, 25 Nov 2021 18:41:19 +0100 Subject: [PATCH 1/4] Set device type on first message received When receiving the first message from a new device, deduce its device type from its logical address if possible and store it in the network. Bug: 205082572 Test: atest HdmiCecNetworkTest Change-Id: I66b0969347e7d292f8222efb250156e0421e60ca Merged-In: I66b0969347e7d292f8222efb250156e0421e60ca --- .../android/server/hdmi/HdmiCecNetwork.java | 34 +++++++++++++++++-- .../server/hdmi/HdmiCecNetworkTest.java | 16 +++++---- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 7ceaa959212e4..180bcdbe7546c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -370,10 +370,12 @@ public class HdmiCecNetwork { // This only applies to TV devices. // Returns true if the policy is set to true, and the device to check does not have // a parent CEC device (which should be the CEC-enabled switch) in the list. + // Devices with an invalid physical address are assumed to NOT be connected to a legacy switch. private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) { return isLocalDeviceAddress(Constants.ADDR_TV) && HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH - && !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches()); + && !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches()) + && info.getPhysicalAddress() != HdmiDeviceInfo.PATH_INVALID; } /** @@ -497,6 +499,34 @@ public class HdmiCecNetwork { return device.getPhysicalAddress() == physicalAddress; } + /** + * Attempts to deduce the device type of a device given its logical address. + * If multiple types are possible, returns {@link HdmiDeviceInfo#DEVICE_RESERVED}. + */ + private static int logicalAddressToDeviceType(int logicalAddress) { + switch (logicalAddress) { + case Constants.ADDR_TV: + return HdmiDeviceInfo.DEVICE_TV; + case Constants.ADDR_RECORDER_1: + case Constants.ADDR_RECORDER_2: + case Constants.ADDR_RECORDER_3: + return HdmiDeviceInfo.DEVICE_RECORDER; + case Constants.ADDR_TUNER_1: + case Constants.ADDR_TUNER_2: + case Constants.ADDR_TUNER_3: + case Constants.ADDR_TUNER_4: + return HdmiDeviceInfo.DEVICE_TUNER; + case Constants.ADDR_PLAYBACK_1: + case Constants.ADDR_PLAYBACK_2: + case Constants.ADDR_PLAYBACK_3: + return HdmiDeviceInfo.DEVICE_PLAYBACK; + case Constants.ADDR_AUDIO_SYSTEM: + return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM; + default: + return HdmiDeviceInfo.DEVICE_RESERVED; + } + } + /** * Passively listen to incoming CEC messages. * @@ -510,7 +540,7 @@ public class HdmiCecNetwork { if (getCecDeviceInfo(sourceAddress) == null) { HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress, HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID, - HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID, + logicalAddressToDeviceType(sourceAddress), Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(sourceAddress)); addCecDevice(newDevice); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java index b1998f55c72ee..c478c23433610 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java @@ -100,6 +100,8 @@ public class HdmiCecNetworkTest { new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false); mNativeWrapper.setPortInfo(mHdmiPortInfo); mHdmiCecNetwork.initPortInfo(); + + mHdmiCecNetwork = mHdmiControlService.getHdmiCecNetwork(); } @Test @@ -141,6 +143,7 @@ public class HdmiCecNetworkTest { @Test public void localDevices_verifyOne_tv() { + mHdmiCecNetwork.clearLocalDevices(); mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV, new HdmiCecLocalDeviceTv(mHdmiControlService)); @@ -153,6 +156,7 @@ public class HdmiCecNetworkTest { @Test public void localDevices_verifyOne_playback() { + mHdmiCecNetwork.clearLocalDevices(); mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK, new HdmiCecLocalDevicePlayback(mHdmiControlService)); @@ -175,7 +179,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID); @@ -257,7 +261,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); @@ -278,7 +282,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName); assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo( @@ -298,7 +302,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(vendorId); @@ -429,7 +433,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(updatedVendorId); @@ -470,7 +474,7 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress); assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo( Constants.INVALID_PHYSICAL_ADDRESS); - assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED); + assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID); assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); From c8adf1f70e4a3ea9236fa30e156e03bfdf4a71db Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Mon, 28 Feb 2022 18:03:58 +0100 Subject: [PATCH 2/4] Assign local device portId 0 Before, the local device on a TV would be assigned an invalid portId, which was causing the local device to be removed from the HdmiCecNetwork in some instances. Bug: 213423171 Test: atest HdmiCecNetworkTest Change-Id: I99e1ca1d54a74682c0737deb2ad2163f7db5e0db Merged-In: I99e1ca1d54a74682c0737deb2ad2163f7db5e0db --- .../com/android/server/hdmi/HdmiCecNetwork.java | 7 +++++-- .../android/server/hdmi/HdmiCecNetworkTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 180bcdbe7546c..1234d7fa08325 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -865,10 +865,13 @@ public class HdmiCecNetwork { * on the current device. */ int physicalAddressToPortId(int path) { + int physicalAddress = getPhysicalAddress(); + if (path == physicalAddress) { + // The local device isn't connected to any port; assign portId 0 + return Constants.CEC_SWITCH_HOME; + } int mask = 0xF000; int finalMask = 0xF000; - int physicalAddress; - physicalAddress = getPhysicalAddress(); int maskedAddress = physicalAddress; while (maskedAddress != 0) { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java index c478c23433610..017039053222d 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java @@ -141,6 +141,22 @@ public class HdmiCecNetworkTest { Constants.INVALID_PORT_ID); } + @Test + public void physicalAddressToPort_localDevice_weAreSourceDevice() { + mNativeWrapper.setPhysicalAddress(0x2000); + mHdmiCecNetwork.initPortInfo(); + assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2000)) + .isEqualTo(Constants.CEC_SWITCH_HOME); + } + + @Test + public void physicalAddressToPort_localDevice_weAreTv() { + mNativeWrapper.setPhysicalAddress(0x0000); + mHdmiCecNetwork.initPortInfo(); + assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x0000)) + .isEqualTo(Constants.CEC_SWITCH_HOME); + } + @Test public void localDevices_verifyOne_tv() { mHdmiCecNetwork.clearLocalDevices(); From c188a5688898bc801b80d2dfee03e1bb5bf6b035 Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Wed, 23 Mar 2022 09:02:20 +0100 Subject: [PATCH 3/4] Don't remove devices on onHotplug() This CL affects TV panels and Audio Systems only. Before HdmiCecNetwork existed: devices were removed when HotplugDetectionAction detected a hotplug out, and TIF was informed. Before this CL: devices were removed from the CEC network onHotplug, but the listener to inform TIF wasn't invoked. This was causing multiple issues. After: on TV panels and Audio Systems, only remove devices when HotplugDetectionAction detects a hotplug out, just like before HdmiCecNetwork existed. Test: atest Bug:213417037 Change-Id: I4181b4650b70101da44fd205e4df9eb566f74496 Merged-In: I4181b4650b70101da44fd205e4df9eb566f74496 --- .../hdmi/IHdmiDeviceEventListener.aidl | 2 + .../hdmi/HdmiCecLocalDevicePlayback.java | 1 + .../server/hdmi/HdmiCecLocalDeviceTv.java | 5 + .../android/server/hdmi/HdmiCecNetwork.java | 7 +- .../server/hdmi/HdmiControlService.java | 4 - .../server/hdmi/HotplugDetectionAction.java | 6 +- .../hdmi/HdmiCecLocalDevicePlaybackTest.java | 28 ++++ .../server/hdmi/HdmiCecLocalDeviceTvTest.java | 122 ++++++++++++++++++ 8 files changed, 167 insertions(+), 8 deletions(-) diff --git a/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl b/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl index 69f29111635c5..b2ddef9c29a61 100644 --- a/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl @@ -22,6 +22,8 @@ import android.hardware.hdmi.HdmiDeviceInfo; * Callback interface definition for HDMI client to get informed of * the CEC logical device status change event. * + * Only to be used on TV panel and Audio System devices (b/226317598). + * * @hide */ oneway interface IHdmiDeviceEventListener { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 40718585c484f..1be8d327c4053 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -127,6 +127,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { // We'll not invalidate the active source on the hotplug event to pass CETC 11.2.2-2 ~ 3. if (!connected) { getWakeLock().release(); + mService.getHdmiCecNetwork().removeDevicesConnectedToPort(portId); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 3d218cffc5df1..b1ffbda8d7730 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1218,6 +1218,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void onHotplug(int portId, boolean connected) { assertRunOnServiceThread(); + + if (!connected) { + mService.getHdmiCecNetwork().removeCecSwitches(portId); + } + // Turning System Audio Mode off when the AVR is unlugged or standby. // When the device is not unplugged but reawaken from standby, we check if the System // Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly. diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 1234d7fa08325..04a7259a2b2df 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -698,7 +698,7 @@ public class HdmiCecNetwork { return mCecSwitches; } - void removeDevicesConnectedToPort(int portId) { + void removeCecSwitches(int portId) { Iterator it = mCecSwitches.iterator(); while (it.hasNext()) { int path = it.next(); @@ -707,6 +707,11 @@ public class HdmiCecNetwork { it.remove(); } } + } + + void removeDevicesConnectedToPort(int portId) { + removeCecSwitches(portId); + List toRemove = new ArrayList<>(); for (int i = 0; i < mDeviceInfos.size(); i++) { int key = mDeviceInfos.keyAt(i); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8dadf5a8d20dc..a16bd3b5e6ba3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1251,10 +1251,6 @@ public class HdmiControlService extends SystemService { device.onHotplug(portId, connected); } - if (!connected) { - mHdmiCecNetwork.removeDevicesConnectedToPort(portId); - } - announceHotplugEvent(portId, connected); } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index 4c4c9783fab0f..3d771646df6aa 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -36,9 +36,9 @@ import java.util.List; final class HotplugDetectionAction extends HdmiCecFeatureAction { private static final String TAG = "HotPlugDetectionAction"; - private static final int POLLING_INTERVAL_MS = 5000; - private static final int TIMEOUT_COUNT = 3; - private static final int AVR_COUNT_MAX = 3; + public static final int POLLING_INTERVAL_MS = 5000; + public static final int TIMEOUT_COUNT = 3; + public static final int AVR_COUNT_MAX = 3; // State in which waits for next polling private static final int STATE_WAIT_FOR_NEXT_POLLING = 1; 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 524ad62823974..77af22598c43e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -1789,4 +1789,32 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortPressed); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortReleased); } + + @Test + public void onHotplugClearsDevices() { + mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + // Add a device to the network and assert that this device is included in the list of + // devices. + HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo( + Constants.ADDR_PLAYBACK_3, + 0x1000, + PORT_1, + HdmiDeviceInfo.DEVICE_PLAYBACK, + 0x1000, + "Playback 3", + HdmiControlManager.POWER_STATUS_ON); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback); + mTestLooper.dispatchAll(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + + // HAL detects a hotplug out. Assert that this device gets removed from the list of devices. + mHdmiControlService.onHotplug(PORT_1, false); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 59711a62f3f34..06cd0bce659e6 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -56,12 +56,14 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.List; @SmallTest @RunWith(JUnit4.class) /** Tests for {@link HdmiCecLocalDeviceTv} class. */ public class HdmiCecLocalDeviceTvTest { private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1; + private static final int PORT_1 = 1; private HdmiControlService mHdmiControlService; private HdmiCecController mHdmiCecController; @@ -73,6 +75,25 @@ public class HdmiCecLocalDeviceTvTest { private int mTvPhysicalAddress; private int mTvLogicalAddress; private boolean mWokenUp; + private List mDeviceEventListeners = new ArrayList<>(); + + private class DeviceEventListener { + private HdmiDeviceInfo mDevice; + private int mStatus; + + DeviceEventListener(HdmiDeviceInfo device, int status) { + this.mDevice = device; + this.mStatus = status; + } + + int getStatus() { + return mStatus; + } + + HdmiDeviceInfo getDeviceInfo() { + return mDevice; + } + } @Mock private IPowerManager mIPowerManagerMock; @@ -124,6 +145,11 @@ public class HdmiCecLocalDeviceTvTest { AudioManager getAudioManager() { return mAudioManager; } + + @Override + void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) { + mDeviceEventListeners.add(new DeviceEventListener(device, status)); + } }; mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService); @@ -596,4 +622,100 @@ public class HdmiCecLocalDeviceTvTest { verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); } + + @Test + public void hotplugDetectionActionClearsDevices() { + mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + // Add a device to the network and assert that this device is included in the list of + // devices. + HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo( + Constants.ADDR_PLAYBACK_2, + 0x1000, + PORT_1, + HdmiDeviceInfo.DEVICE_PLAYBACK, + 0x1000, + "Playback 2", + HdmiControlManager.POWER_STATUS_ON); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback); + mTestLooper.dispatchAll(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + mDeviceEventListeners.clear(); + assertThat(mDeviceEventListeners.size()).isEqualTo(0); + + // HAL detects a hotplug out. Assert that this device stays in the list of devices. + mHdmiControlService.onHotplug(PORT_1, false); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + assertThat(mDeviceEventListeners).isEmpty(); + mTestLooper.dispatchAll(); + // Make the device not acknowledge the poll message sent by the HotplugDetectionAction. + // Assert that this device is removed from the list of devices. + mNativeWrapper.setPollAddressResponse(Constants.ADDR_PLAYBACK_2, SendMessageResult.NACK); + for (int pollCount = 0; pollCount < HotplugDetectionAction.TIMEOUT_COUNT; pollCount++) { + mTestLooper.moveTimeForward(HotplugDetectionAction.POLLING_INTERVAL_MS); + mTestLooper.dispatchAll(); + } + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + assertThat(mDeviceEventListeners.size()).isEqualTo(1); + assertThat(mDeviceEventListeners.get(0).getStatus()) + .isEqualTo(HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); + HdmiDeviceInfo removedDeviceInfo = mDeviceEventListeners.get(0).getDeviceInfo(); + assertThat(removedDeviceInfo.getPortId()).isEqualTo(PORT_1); + assertThat(removedDeviceInfo.getLogicalAddress()).isEqualTo(Constants.ADDR_PLAYBACK_2); + assertThat(removedDeviceInfo.getPhysicalAddress()).isEqualTo(0x1000); + assertThat(removedDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK); + } + + @Test + public void hotplugDetectionActionClearsDevices_AudioSystem() { + mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + // Add a device to the network and assert that this device is included in the list of + // devices. + HdmiDeviceInfo infoAudioSystem = new HdmiDeviceInfo( + ADDR_AUDIO_SYSTEM, + 0x1000, + PORT_1, + HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, + 0x1000, + "Audio System", + HdmiControlManager.POWER_STATUS_ON); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoAudioSystem); + mTestLooper.dispatchAll(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + mDeviceEventListeners.clear(); + assertThat(mDeviceEventListeners.size()).isEqualTo(0); + + // HAL detects a hotplug out. Assert that this device stays in the list of devices. + mHdmiControlService.onHotplug(PORT_1, false); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + assertThat(mDeviceEventListeners).isEmpty(); + mTestLooper.dispatchAll(); + // Make the device not acknowledge the poll message sent by the HotplugDetectionAction. + // Assert that this device is removed from the list of devices. + mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.NACK); + for (int pollCount = 0; pollCount < HotplugDetectionAction.TIMEOUT_COUNT; pollCount++) { + mTestLooper.moveTimeForward(HotplugDetectionAction.POLLING_INTERVAL_MS); + mTestLooper.dispatchAll(); + } + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + assertThat(mDeviceEventListeners.size()).isEqualTo(1); + assertThat(mDeviceEventListeners.get(0).getStatus()) + .isEqualTo(HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); + HdmiDeviceInfo removedDeviceInfo = mDeviceEventListeners.get(0).getDeviceInfo(); + assertThat(removedDeviceInfo.getPortId()).isEqualTo(PORT_1); + assertThat(removedDeviceInfo.getLogicalAddress()).isEqualTo(Constants.ADDR_AUDIO_SYSTEM); + assertThat(removedDeviceInfo.getPhysicalAddress()).isEqualTo(0x1000); + assertThat(removedDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + } } From a598b9362220c8ef719975cf9f69e278888a2157 Mon Sep 17 00:00:00 2001 From: Nathalie Le Clair Date: Wed, 23 Mar 2022 14:55:53 +0100 Subject: [PATCH 4/4] Only invoke listener once physical address becomes known Test: atest Bug: 205082572 Change-Id: I46fb8cfe2825c2c26207b79965fbbace41fc7793 Merged-In: I46fb8cfe2825c2c26207b79965fbbace41fc7793 --- .../android/server/hdmi/HdmiCecNetwork.java | 19 ++++++-- .../server/hdmi/HdmiCecLocalDeviceTvTest.java | 48 +++++++++++++++++++ .../server/hdmi/HdmiCecNetworkTest.java | 36 +++++++------- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 04a7259a2b2df..72b79f401a99d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -269,7 +269,10 @@ public class HdmiCecNetwork { // The addition of a local device should not notify listeners return; } - if (old == null) { + if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { + // Don't notify listeners of devices that haven't reported their physical address yet + return; + } else if (old == null || old.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); } else if (!old.equals(info)) { @@ -296,7 +299,10 @@ public class HdmiCecNetwork { assertRunOnServiceThread(); HdmiDeviceInfo old = addDeviceInfo(info); - if (old == null) { + if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { + // Don't notify listeners of devices that haven't reported their physical address yet + return; + } else if (old == null || old.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); } else if (!old.equals(info)) { @@ -389,6 +395,10 @@ public class HdmiCecNetwork { HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address)); localDevice.mCecMessageCache.flushMessagesFrom(address); + if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { + // Don't notify listeners of devices that haven't reported their physical address yet + return; + } invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); } @@ -823,7 +833,10 @@ public class HdmiCecNetwork { public void clearDeviceList() { assertRunOnServiceThread(); for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) { - if (info.getPhysicalAddress() == getPhysicalAddress()) { + if (info.getPhysicalAddress() == getPhysicalAddress() + || info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) { + // Don't notify listeners of local devices or devices that haven't reported their + // physical address yet continue; } invokeDeviceEventListener(info, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 06cd0bce659e6..0afd27c48ee60 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -19,6 +19,7 @@ import static com.android.server.hdmi.Constants.ABORT_UNRECOGNIZED_OPCODE; import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; +import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_RECORDER_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -718,4 +719,51 @@ public class HdmiCecLocalDeviceTvTest { assertThat(removedDeviceInfo.getPhysicalAddress()).isEqualTo(0x1000); assertThat(removedDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); } + + @Test + public void listenerInvokedIfPhysicalAddressReported() { + mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + assertThat(mDeviceEventListeners.size()).isEqualTo(1); + assertThat(mDeviceEventListeners.get(0).getStatus()) + .isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); + } + + @Test + public void listenerNotInvokedIfPhysicalAddressUnknown() { + mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .isEmpty(); + HdmiCecMessage setOsdName = HdmiCecMessageBuilder.buildSetOsdNameCommand( + ADDR_PLAYBACK_2, ADDR_TV, "Playback 2"); + mNativeWrapper.onCecMessage(setOsdName); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + assertThat(mDeviceEventListeners).isEmpty(); + + // When the device reports its physical address, the listener eventually is invoked. + HdmiCecMessage reportPhysicalAddress = + HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); + mNativeWrapper.onCecMessage(reportPhysicalAddress); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)) + .hasSize(1); + assertThat(mDeviceEventListeners.size()).isEqualTo(1); + assertThat(mDeviceEventListeners.get(0).getStatus()) + .isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); + + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java index 017039053222d..c617d217ec94e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java @@ -202,20 +202,19 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo( HdmiControlManager.POWER_STATUS_UNKNOWN); - assertThat(mDeviceEventListenerStatuses).containsExactly( - HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test public void cecDevices_tracking_logicalAddressOnly_doesntNotifyAgain() throws Exception { int logicalAddress = Constants.ADDR_PLAYBACK_1; + int physicalAddress = 0x1000; mHdmiCecNetwork.handleCecMessage( - HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000)); + HdmiCecMessageBuilder.buildActiveSource(logicalAddress, physicalAddress)); mHdmiCecNetwork.handleCecMessage( - HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000)); + HdmiCecMessageBuilder.buildActiveSource(logicalAddress, physicalAddress)); - assertThat(mDeviceEventListenerStatuses).containsExactly( - HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test @@ -239,6 +238,9 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID); assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo( HdmiControlManager.POWER_STATUS_UNKNOWN); + + assertThat(mDeviceEventListenerStatuses).containsExactly( + HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); } @Test @@ -256,11 +258,10 @@ public class HdmiCecNetworkTest { physicalAddress, type)); - // ADD for logical address first detected - // UPDATE for updating device with physical address + // ADD for physical address first detected + // no UPDATE, since physical address didn't change assertThat(mDeviceEventListenerStatuses).containsExactly( - HdmiControlManager.DEVICE_EVENT_ADD_DEVICE, - HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE); + HdmiControlManager.DEVICE_EVENT_ADD_DEVICE); } @Test @@ -282,6 +283,8 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getDisplayName()).isEqualTo( HdmiUtils.getDefaultDeviceName(logicalAddress)); assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus); + + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test @@ -303,6 +306,8 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName); assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo( HdmiControlManager.POWER_STATUS_UNKNOWN); + + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test @@ -324,6 +329,8 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getVendorId()).isEqualTo(vendorId); assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo( HdmiControlManager.POWER_STATUS_UNKNOWN); + + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test @@ -382,12 +389,10 @@ public class HdmiCecNetworkTest { assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(updatedPhysicalAddress); assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type); - // ADD for logical address first detected - // UPDATE for updating device with physical address + // ADD for physical address first detected // UPDATE for updating device with new physical address assertThat(mDeviceEventListenerStatuses).containsExactly( HdmiControlManager.DEVICE_EVENT_ADD_DEVICE, - HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE); } @@ -471,9 +476,8 @@ public class HdmiCecNetworkTest { assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).isEmpty(); - assertThat(mDeviceEventListenerStatuses).containsExactly( - HdmiControlManager.DEVICE_EVENT_ADD_DEVICE, - HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE); + // Physical address never got reported, so no listeners are triggered + assertThat(mDeviceEventListenerStatuses).isEmpty(); } @Test