Refactor the Active Source updating logic in CEC devices.
ag/5108161 Move local Active Source to control service since each device only needs one local Active Source. Easier to update. But each device needs multiple mIsActiveSource for each device type since only one of them should be the Active Source. When Active Source can be handled only by one device type, like OneTouchPlay, call setAndBroadcastActiveSourceFromOneDeviceType in service to update all the device types. When receiving broadcast messages, all of the device types can handle Active Source updating in their own logic by calling setAndBroadcastActiveSource in service. Test: atest com.android.server.hdmi Change-Id: I88ce68719ae02c7a847dabb6c558abdb6840b3fa
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <Routing Change> and <Request Active Source>
|
||||
// in a row, and then changes the input to the internal source if there is no
|
||||
// <Active Source> 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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user