diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 528e0a4e4940f..c1ac1af339efb 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -133,9 +133,6 @@ abstract class HdmiCecLocalDevice { return s.toString(); } } - // Logical address of the active source. - @GuardedBy("mLock") - protected final ActiveSource mActiveSource = new ActiveSource(); // Active routing path. Physical address of the active source but not all the time, such as // when the new active source does not claim itself to be one. Note that we don't keep @@ -867,9 +864,7 @@ abstract class HdmiCecLocalDevice { } ActiveSource getActiveSource() { - synchronized (mLock) { - return mActiveSource; - } + return mService.getActiveSource(); } void setActiveSource(ActiveSource newActive) { @@ -881,10 +876,7 @@ abstract class HdmiCecLocalDevice { } void setActiveSource(int logicalAddress, int physicalAddress) { - synchronized (mLock) { - mActiveSource.logicalAddress = logicalAddress; - mActiveSource.physicalAddress = physicalAddress; - } + mService.setActiveSource(logicalAddress, physicalAddress); mService.setLastInputForMhl(Constants.INVALID_PORT_ID); } @@ -1042,7 +1034,7 @@ abstract class HdmiCecLocalDevice { pw.println("mAddress: " + mAddress); pw.println("mPreferredAddress: " + mPreferredAddress); pw.println("mDeviceInfo: " + mDeviceInfo); - pw.println("mActiveSource: " + mActiveSource); + pw.println("mActiveSource: " + getActiveSource()); pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 73e0b63676551..086a27a0a7acd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -676,12 +676,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } if (getLocalActivePort() == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) { routeToInputFromPortId(Constants.CEC_SWITCH_HOME); - if (mService.playback() != null) { - mService.playback().setAndBroadcastActiveSource( - message, mService.getPhysicalAddress()); - } else { - setAndBroadcastActiveSource(message, mService.getPhysicalAddress()); - } + mService.setAndBroadcastActiveSourceFromOneDeviceType( + message.getSource(), mService.getPhysicalAddress()); return; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 593cf5908bbe2..9bb95384fedcd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -48,8 +48,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { private static final boolean SET_MENU_LANGUAGE = SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false); - private boolean mIsActiveSource = false; - // Used to keep the device awake while it is the active source. For devices that // cannot wake up via CEC commands, this address the inconvenience of having to // turn them on. True by default, and can be disabled (i.e. device can go to sleep @@ -220,53 +218,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { return super.handleUserControlPressed(message); } - @Override - @ServiceThreadOnly - protected boolean handleSetStreamPath(HdmiCecMessage message) { - assertRunOnServiceThread(); - int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); - // If current device is the target path, set to Active Source. - // If the path is under the current device, should switch - int port = getLocalPortFromPhysicalAddress(physicalAddress); - if (port == 0) { - setIsActiveSource(true); - maySendActiveSource(message.getSource()); - wakeUpIfActiveSource(); - } else if (port > 0) { - // Wake up the device if the power is in standby mode for routing - if (mService.isPowerStandbyOrTransient()) { - mService.wakeUp(); - } - routeToPort(port); - } - return true; - } - - // Samsung model we tested sends and - // in a row, and then changes the input to the internal source if there is no - // in response. To handle this, we'll set ActiveSource aggressively. - @Override - @ServiceThreadOnly - protected boolean handleRoutingChange(HdmiCecMessage message) { - assertRunOnServiceThread(); - int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); - maySetActiveSource(newPath); - return true; // Broadcast message. - } - - @Override - @ServiceThreadOnly - protected boolean handleRoutingInformation(HdmiCecMessage message) { - assertRunOnServiceThread(); - int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); - maySetActiveSource(physicalAddress); - return true; // Broadcast message. - } - - private void maySetActiveSource(int physicalAddress) { - setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); - } - @Override protected void wakeUpIfActiveSource() { if (!mIsActiveSource) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 8d55299243b2a..8dfab486aa9fb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -37,7 +37,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDeviceSource"; // Indicate if current device is Active Source or not - private boolean mIsActiveSource = false; + @VisibleForTesting + protected boolean mIsActiveSource = false; // Device has cec switch functionality or not. // Default is false. @@ -113,7 +114,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { int logicalAddress = message.getSource(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); - if (!mActiveSource.equals(activeSource)) { + if (!getActiveSource().equals(activeSource)) { setActiveSource(activeSource); } setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); @@ -185,24 +186,13 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { // do nothing } - // Active source claiming needs to be handled in the parent class - // since we decide who will be the active source when the device supports + // Active source claiming needs to be handled in Service + // since service can decide who will be the active source when the device supports // multiple device types in this method. // This method should only be called when the device can be the active source. protected void setAndBroadcastActiveSource(HdmiCecMessage message, int physicalAddress) { - // If the device has both playback and audio system logical addresses, - // playback will claim active source. Otherwise audio system will. - HdmiCecLocalDevice deviceToBeActiveSource = mService.playback(); - if (deviceToBeActiveSource == null) { - deviceToBeActiveSource = mService.audioSystem(); - } - if (this == deviceToBeActiveSource) { - ActiveSource activeSource = ActiveSource.of(mAddress, physicalAddress); - setIsActiveSource(true); - setActiveSource(activeSource); - wakeUpIfActiveSource(); - maySendActiveSource(message.getSource()); - } + mService.setAndBroadcastActiveSource( + message, physicalAddress, getDeviceInfo().getDeviceType()); } @ServiceThreadOnly @@ -211,13 +201,6 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { mIsActiveSource = on; } - @ServiceThreadOnly - // Check if current device is the Active Source - boolean isActiveSource() { - assertRunOnServiceThread(); - return mIsActiveSource; - } - protected void wakeUpIfActiveSource() { if (!mIsActiveSource) { return; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 25ca27836aa7d..9e1250e1843d9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -47,11 +47,13 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; + import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; @@ -307,7 +309,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { private void handleSelectInternalSource() { assertRunOnServiceThread(); // Seq #18 - if (mService.isControlEnabled() && mActiveSource.logicalAddress != mAddress) { + if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) { updateActiveSource(mAddress, mService.getPhysicalAddress()); if (mSkipRoutingControl) { mSkipRoutingControl = false; @@ -329,7 +331,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { void updateActiveSource(ActiveSource newActive) { assertRunOnServiceThread(); // Seq #14 - if (mActiveSource.equals(newActive)) { + if (getActiveSource().equals(newActive)) { return; } setActiveSource(newActive); @@ -402,7 +404,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS); return; } - mActiveSource.invalidate(); + getActiveSource().invalidate(); if (!mService.isControlEnabled()) { setActivePortId(portId); invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE); @@ -518,7 +520,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } else { // No HDMI port to switch to was found. Notify the input change listers to // switch to the lastly shown internal input. - mActiveSource.invalidate(); + getActiveSource().invalidate(); setActivePath(Constants.INVALID_PHYSICAL_ADDRESS); mService.invokeInputChangeListener(HdmiDeviceInfo.INACTIVE_DEVICE); } @@ -685,7 +687,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { byte[] params = message.getParams(); int currentPath = HdmiUtils.twoBytesToInt(params); if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) { - mActiveSource.invalidate(); + getActiveSource().invalidate(); removeAction(RoutingControlAction.class); int newPath = HdmiUtils.twoBytesToInt(params, 2); addAndStartAction(new RoutingControlAction(this, newPath, true, null)); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bb96a519c949f..53d84a86a95ad 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -140,6 +140,10 @@ public class HdmiControlService extends SystemService { static final int STANDBY_SCREEN_OFF = 0; static final int STANDBY_SHUTDOWN = 1; + // Logical address of the active source. + @GuardedBy("mLock") + protected final ActiveSource mActiveSource = new ActiveSource(); + private static final boolean isHdmiCecNeverClaimPlaybackLogicAddr = SystemProperties.getBoolean( Constants.PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS, false); @@ -2488,6 +2492,77 @@ public class HdmiControlService extends SystemService { setLastInputForMhl(Constants.INVALID_PORT_ID); } + ActiveSource getActiveSource() { + synchronized (mLock) { + return mActiveSource; + } + } + + void setActiveSource(int logicalAddress, int physicalAddress) { + synchronized (mLock) { + mActiveSource.logicalAddress = logicalAddress; + mActiveSource.physicalAddress = physicalAddress; + } + } + + // This method should only be called when the device can be the active source + // and all the device types call into this method. + // For example, when receiving broadcast messages, all the device types will call this + // method but only one of them will be the Active Source. + protected void setAndBroadcastActiveSource( + HdmiCecMessage message, int physicalAddress, int deviceType) { + // If the device has both playback and audio system logical addresses, + // playback will claim active source. Otherwise audio system will. + if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) { + HdmiCecLocalDevicePlayback playback = playback(); + playback.setIsActiveSource(true); + playback.wakeUpIfActiveSource(); + playback.maySendActiveSource(message.getSource()); + setActiveSource(playback.mAddress, physicalAddress); + } + + if (deviceType == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { + HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); + if (playback() != null) { + audioSystem.setIsActiveSource(false); + } else { + audioSystem.setIsActiveSource(true); + audioSystem.wakeUpIfActiveSource(); + audioSystem.maySendActiveSource(message.getSource()); + setActiveSource(audioSystem.mAddress, physicalAddress); + } + } + } + + // This method should only be called when the device can be the active source + // and only one of the device types calls into this method. + // For example, when receiving One Touch Play, only playback device handles it + // and this method updates Active Source in all the device types sharing the same + // Physical Address. + protected void setAndBroadcastActiveSourceFromOneDeviceType( + int sourceAddress, int physicalAddress) { + // If the device has both playback and audio system logical addresses, + // playback will claim active source. Otherwise audio system will. + HdmiCecLocalDevicePlayback playback = playback(); + HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); + if (playback != null) { + playback.setIsActiveSource(true); + playback.wakeUpIfActiveSource(); + playback.maySendActiveSource(sourceAddress); + if (audioSystem != null) { + audioSystem.setIsActiveSource(false); + } + setActiveSource(playback.mAddress, physicalAddress); + } else { + if (audioSystem != null) { + audioSystem.setIsActiveSource(true); + audioSystem.wakeUpIfActiveSource(); + audioSystem.maySendActiveSource(sourceAddress); + setActiveSource(audioSystem.mAddress, physicalAddress); + } + } + } + @ServiceThreadOnly void setLastInputForMhl(int portId) { assertRunOnServiceThread(); diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index 46611dd1b66ea..1908d69d4e6d0 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -83,11 +83,10 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { } private void broadcastActiveSource() { - sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath())); // Because only source device can create this action, it's safe to cast. HdmiCecLocalDeviceSource source = source(); - source.setIsActiveSource(true); - source.setActiveSource(getSourceAddress(), getSourcePath()); + source.mService.setAndBroadcastActiveSourceFromOneDeviceType( + mTargetAddress, getSourcePath()); // Set local active port to HOME when One Touch Play. source.setLocalActivePort(Constants.CEC_SWITCH_HOME); } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java index a6e69656a9567..7e0890fe34a2e 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java @@ -41,7 +41,7 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction { @Override boolean start() { - if (audioSystem().mActiveSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) { + if (audioSystem().getActiveSource().physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) { mState = STATE_WAITING_FOR_ACTIVE_SOURCE; addTimer(mState, HdmiConfig.TIMEOUT_MS); sendRequestActiveSource(); @@ -60,10 +60,8 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction { return false; } mActionTimer.clearTimerMessage(); - int physicalAddress = HdmiUtils.twoBytesToInt(cmd.getParams()); - if (physicalAddress != getSourcePath()) { - audioSystem().setActiveSource(cmd.getSource(), physicalAddress); - } + // Broadcast message is also handled by other device types + audioSystem().handleActiveSource(cmd); mState = STATE_WAITING_FOR_TV_SUPPORT; queryTvSystemAudioModeSupport(); return true;