Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +22 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.CallSuper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.os.Handler; import android.sysprop.HdmiProperties; import android.util.Slog; Loading Loading @@ -65,8 +66,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @GuardedBy("mLock") protected boolean mRoutingControlFeatureEnabled; private Handler mHandler; protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) { super(service, deviceType); mHandler = new Handler(service.getServiceLooper()); } @ServiceThreadOnly Loading Loading @@ -243,12 +247,26 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { protected int handleSetStreamPath(HdmiCecMessage message) { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); int localDevicePhysicalAddress = mService.getPhysicalAddress(); // If <Set Stream Path> targeting an ancestor address is received while this device is // the active source, wait to see if a new Active Source is declared, as a parent switch // might be changing paths. // E.g. TV [0.0.0.0] ------ AVR [3.0.0.0] ------ Playback [3.1.0.0] (Active Source) // TV sends <Set Stream Path> [3.0.0.0] // AVR sends <Routing Information> [3.2.0.0] -> Playback [3.1.0.0] is no longer AS. // AVR sends <Routing Information> [3.1.0.0] -> Playback [3.1.0.0] stays <AS>. // AVR sends no relevant CEC message -> Playback [3.1.0.0] stays <AS>. if (isActiveSource() && HdmiUtils.isTailOfActivePath(localDevicePhysicalAddress, physicalAddress)) { return Constants.HANDLED; } if (physicalAddress == localDevicePhysicalAddress && mService.isPlaybackDevice()) { // If current device is the target path, set to Active Source. // If the path is under the current device, should switch if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) { setAndBroadcastActiveSource(message, physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()"); } else if (physicalAddress != mService.getPhysicalAddress() || !isActiveSource()) { } else if (physicalAddress != localDevicePhysicalAddress || !isActiveSource()) { // Invalidate the active source if stream path is set to other physical address or // our physical address while not active source setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()"); Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +2 −29 Original line number Diff line number Diff line Loading @@ -690,7 +690,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private boolean handleNewDeviceAtTheTailOfActivePath(int path) { // Seq #22 if (isTailOfActivePath(path, getActivePath())) { if (HdmiUtils.isTailOfActivePath(path, getActivePath())) { int newPath = mService.portIdToPath(getActivePortId()); setActivePath(newPath); startRoutingControl(getActivePath(), newPath, null); Loading @@ -699,33 +699,6 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return false; } /** * Whether the given path is located in the tail of current active path. * * @param path to be tested * @param activePath current active path * @return true if the given path is located in the tail of current active path; otherwise, * false */ static boolean isTailOfActivePath(int path, int activePath) { // If active routing path is internal source, return false. if (activePath == 0) { return false; } for (int i = 12; i >= 0; i -= 4) { int curActivePath = (activePath >> i) & 0xF; if (curActivePath == 0) { return true; } else { int curPath = (path >> i) & 0xF; if (curPath != curActivePath) { return false; } } } return false; } @Override @ServiceThreadOnly @Constants.HandleMessageResult Loading Loading @@ -1391,7 +1364,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { void handleRemoveActiveRoutingPath(int path) { assertRunOnServiceThread(); // Seq #23 if (isTailOfActivePath(path, getActivePath())) { if (HdmiUtils.isTailOfActivePath(path, getActivePath())) { int newPath = mService.portIdToPath(getActivePortId()); startRoutingControl(getActivePath(), newPath, null); } Loading services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +5 −0 Original line number Diff line number Diff line Loading @@ -872,6 +872,11 @@ public class HdmiCecNetwork { return mPhysicalAddress; } @VisibleForTesting protected void invalidatePhysicalAddress() { mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS; } @ServiceThreadOnly public void clear() { assertRunOnServiceThread(); Loading services/core/java/com/android/server/hdmi/HdmiUtils.java +27 −0 Original line number Diff line number Diff line Loading @@ -724,6 +724,33 @@ final class HdmiUtils { } } /** * Whether the given path is located in the tail of current active path. * * @param path to be tested * @param activePath current active path * @return true if the given path is located in the tail of current active path; otherwise, * false */ static boolean isTailOfActivePath(int path, int activePath) { // If active routing path is internal source, return false. if (activePath == 0) { return false; } for (int i = 12; i >= 0; i -= 4) { int curActivePath = (activePath >> i) & 0xF; if (curActivePath == 0) { return true; } else { int curPath = (path >> i) & 0xF; if (curPath != curActivePath) { return false; } } } return false; } // Device configuration of its supported Codecs and their Short Audio Descriptors. public static class DeviceConfig { /** Name of the device. Should be {@link Constants.AudioDevice}. **/ Loading services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +55 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.sysprop.HdmiProperties; import android.util.Log; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; Loading Loading @@ -1804,6 +1805,60 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mActiveMediaSessionsPaused).isFalse(); } @Test public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutStillActiveSource() { int playbackPhysicalAddress = 0x3100; mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress); mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress(); mTestLooper.dispatchAll(); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mTestLooper.dispatchAll(); HdmiCecMessage message = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000); assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); assertThat(mPowerManager.isInteractive()).isTrue(); } @Test public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutNotActiveSource() { int playbackPhysicalAddress = 0x3100; mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress); mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress(); mTestLooper.dispatchAll(); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mTestLooper.dispatchAll(); HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000); HdmiCecMessage routingInformation = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x3200); assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPath)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(routingInformation)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse(); } @Test public void oneTouchPlay_PowerControlModeToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( Loading Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +22 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.CallSuper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.os.Handler; import android.sysprop.HdmiProperties; import android.util.Slog; Loading Loading @@ -65,8 +66,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @GuardedBy("mLock") protected boolean mRoutingControlFeatureEnabled; private Handler mHandler; protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) { super(service, deviceType); mHandler = new Handler(service.getServiceLooper()); } @ServiceThreadOnly Loading Loading @@ -243,12 +247,26 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { protected int handleSetStreamPath(HdmiCecMessage message) { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); int localDevicePhysicalAddress = mService.getPhysicalAddress(); // If <Set Stream Path> targeting an ancestor address is received while this device is // the active source, wait to see if a new Active Source is declared, as a parent switch // might be changing paths. // E.g. TV [0.0.0.0] ------ AVR [3.0.0.0] ------ Playback [3.1.0.0] (Active Source) // TV sends <Set Stream Path> [3.0.0.0] // AVR sends <Routing Information> [3.2.0.0] -> Playback [3.1.0.0] is no longer AS. // AVR sends <Routing Information> [3.1.0.0] -> Playback [3.1.0.0] stays <AS>. // AVR sends no relevant CEC message -> Playback [3.1.0.0] stays <AS>. if (isActiveSource() && HdmiUtils.isTailOfActivePath(localDevicePhysicalAddress, physicalAddress)) { return Constants.HANDLED; } if (physicalAddress == localDevicePhysicalAddress && mService.isPlaybackDevice()) { // If current device is the target path, set to Active Source. // If the path is under the current device, should switch if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) { setAndBroadcastActiveSource(message, physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()"); } else if (physicalAddress != mService.getPhysicalAddress() || !isActiveSource()) { } else if (physicalAddress != localDevicePhysicalAddress || !isActiveSource()) { // Invalidate the active source if stream path is set to other physical address or // our physical address while not active source setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()"); Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +2 −29 Original line number Diff line number Diff line Loading @@ -690,7 +690,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private boolean handleNewDeviceAtTheTailOfActivePath(int path) { // Seq #22 if (isTailOfActivePath(path, getActivePath())) { if (HdmiUtils.isTailOfActivePath(path, getActivePath())) { int newPath = mService.portIdToPath(getActivePortId()); setActivePath(newPath); startRoutingControl(getActivePath(), newPath, null); Loading @@ -699,33 +699,6 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return false; } /** * Whether the given path is located in the tail of current active path. * * @param path to be tested * @param activePath current active path * @return true if the given path is located in the tail of current active path; otherwise, * false */ static boolean isTailOfActivePath(int path, int activePath) { // If active routing path is internal source, return false. if (activePath == 0) { return false; } for (int i = 12; i >= 0; i -= 4) { int curActivePath = (activePath >> i) & 0xF; if (curActivePath == 0) { return true; } else { int curPath = (path >> i) & 0xF; if (curPath != curActivePath) { return false; } } } return false; } @Override @ServiceThreadOnly @Constants.HandleMessageResult Loading Loading @@ -1391,7 +1364,7 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { void handleRemoveActiveRoutingPath(int path) { assertRunOnServiceThread(); // Seq #23 if (isTailOfActivePath(path, getActivePath())) { if (HdmiUtils.isTailOfActivePath(path, getActivePath())) { int newPath = mService.portIdToPath(getActivePortId()); startRoutingControl(getActivePath(), newPath, null); } Loading
services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +5 −0 Original line number Diff line number Diff line Loading @@ -872,6 +872,11 @@ public class HdmiCecNetwork { return mPhysicalAddress; } @VisibleForTesting protected void invalidatePhysicalAddress() { mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS; } @ServiceThreadOnly public void clear() { assertRunOnServiceThread(); Loading
services/core/java/com/android/server/hdmi/HdmiUtils.java +27 −0 Original line number Diff line number Diff line Loading @@ -724,6 +724,33 @@ final class HdmiUtils { } } /** * Whether the given path is located in the tail of current active path. * * @param path to be tested * @param activePath current active path * @return true if the given path is located in the tail of current active path; otherwise, * false */ static boolean isTailOfActivePath(int path, int activePath) { // If active routing path is internal source, return false. if (activePath == 0) { return false; } for (int i = 12; i >= 0; i -= 4) { int curActivePath = (activePath >> i) & 0xF; if (curActivePath == 0) { return true; } else { int curPath = (path >> i) & 0xF; if (curPath != curActivePath) { return false; } } } return false; } // Device configuration of its supported Codecs and their Short Audio Descriptors. public static class DeviceConfig { /** Name of the device. Should be {@link Constants.AudioDevice}. **/ Loading
services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +55 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.sysprop.HdmiProperties; import android.util.Log; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; Loading Loading @@ -1804,6 +1805,60 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mActiveMediaSessionsPaused).isFalse(); } @Test public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutStillActiveSource() { int playbackPhysicalAddress = 0x3100; mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress); mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress(); mTestLooper.dispatchAll(); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mTestLooper.dispatchAll(); HdmiCecMessage message = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000); assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue(); assertThat(mPowerManager.isInteractive()).isTrue(); } @Test public void handleSetStreamPath_ancestorPathReceivedWhileActive_dutNotActiveSource() { int playbackPhysicalAddress = 0x3100; mNativeWrapper.setPhysicalAddress(playbackPhysicalAddress); mHdmiControlService.getHdmiCecNetwork().invalidatePhysicalAddress(); mTestLooper.dispatchAll(); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress, playbackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest"); mTestLooper.dispatchAll(); HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x3000); HdmiCecMessage routingInformation = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x3200); assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPath)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(routingInformation)) .isEqualTo(Constants.HANDLED); mTestLooper.dispatchAll(); assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse(); } @Test public void oneTouchPlay_PowerControlModeToTv() { mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( Loading