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:
Amy
2018-09-25 10:56:31 -07:00
committed by shubang
parent cd18d288fe
commit 123ec40f7d
8 changed files with 99 additions and 103 deletions

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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));

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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;