Merge "Track HDMI CEC Network"

This commit is contained in:
TreeHugger Robot
2020-10-26 13:43:07 +00:00
committed by Android (Google) Code Review
22 changed files with 1495 additions and 1144 deletions

View File

@@ -339,7 +339,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
// This is to manager CEC device separately in case they don't have address.
if (mIsTvDevice) {
tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
localDevice().mService.getHdmiCecNetwork().updateCecSwitchInfo(current.mLogicalAddress,
current.mDeviceType,
current.mPhysicalAddress);
}
increaseProcessedDeviceCount();

View File

@@ -32,7 +32,6 @@ import android.os.Looper;
import android.os.RemoteException;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -97,7 +96,7 @@ final class HdmiCecController {
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@Override
public boolean test(Integer address) {
return !isAllocatedLocalDeviceAddress(address);
return !mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
}
};
@@ -118,9 +117,6 @@ final class HdmiCecController {
private final HdmiControlService mService;
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
// Stores recent CEC messages and HDMI Hotplug event history for debugging purpose.
private final ArrayBlockingQueue<Dumpable> mMessageHistory =
new ArrayBlockingQueue<>(MAX_HDMI_MESSAGE_HISTORY);
@@ -173,12 +169,6 @@ final class HdmiCecController {
nativeWrapper.setCallback(new HdmiCecCallback());
}
@ServiceThreadOnly
void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
assertRunOnServiceThread();
mLocalDevices.put(deviceType, device);
}
/**
* Allocate a new logical address of the given device type. Allocated
* address will be reported through {@link AllocateAddressCallback}.
@@ -268,17 +258,6 @@ final class HdmiCecController {
return mNativeWrapperImpl.nativeGetPortInfos();
}
/**
* Return the locally hosted logical device of a given type.
*
* @param deviceType logical device type
* @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
* otherwise null.
*/
HdmiCecLocalDevice getLocalDevice(int deviceType) {
return mLocalDevices.get(deviceType);
}
/**
* Add a new logical address to the device. Device's HW should be notified
* when a new logical address is assigned to a device, so that it can accept
@@ -307,18 +286,9 @@ final class HdmiCecController {
@ServiceThreadOnly
void clearLogicalAddress() {
assertRunOnServiceThread();
for (int i = 0; i < mLocalDevices.size(); ++i) {
mLocalDevices.valueAt(i).clearAddress();
}
mNativeWrapperImpl.nativeClearLogicalAddress();
}
@ServiceThreadOnly
void clearLocalDevices() {
assertRunOnServiceThread();
mLocalDevices.clear();
}
/**
* Return the physical address of the device.
*
@@ -428,17 +398,6 @@ final class HdmiCecController {
runDevicePolling(sourceAddress, pollingCandidates, retryCount, callback, allocated);
}
/**
* Return a list of all {@link HdmiCecLocalDevice}s.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
@ServiceThreadOnly
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
return HdmiUtils.sparseArrayToList(mLocalDevices);
}
private List<Integer> pickPollCandidates(int pickStrategy) {
int strategy = pickStrategy & Constants.POLL_STRATEGY_MASK;
Predicate<Integer> pickPredicate = null;
@@ -474,17 +433,6 @@ final class HdmiCecController {
return pollingCandidates;
}
@ServiceThreadOnly
private boolean isAllocatedLocalDeviceAddress(int address) {
assertRunOnServiceThread();
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
return true;
}
}
return false;
}
@ServiceThreadOnly
private void runDevicePolling(final int sourceAddress,
final List<Integer> candidates, final int retryCount,
@@ -578,7 +526,7 @@ final class HdmiCecController {
if (address == Constants.ADDR_BROADCAST) {
return true;
}
return isAllocatedLocalDeviceAddress(address);
return mService.getHdmiCecNetwork().isAllocatedLocalDeviceAddress(address);
}
@ServiceThreadOnly
@@ -682,7 +630,7 @@ final class HdmiCecController {
private int incomingMessageDirection(int srcAddress, int dstAddress) {
boolean sourceIsLocal = false;
boolean destinationIsLocal = false;
for (HdmiCecLocalDevice localDevice : getLocalDeviceList()) {
for (HdmiCecLocalDevice localDevice : mService.getHdmiCecNetwork().getLocalDeviceList()) {
int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
if (logicalAddress == srcAddress) {
sourceIsLocal = true;
@@ -731,24 +679,9 @@ final class HdmiCecController {
}
void dump(final IndentingPrintWriter pw) {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < mLocalDevices.size(); ++i) {
pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
pw.increaseIndent();
mLocalDevices.valueAt(i).dump(pw);
pw.println("Active Source history:");
pw.increaseIndent();
for (Dumpable activeSourceEvent : mLocalDevices.valueAt(i).getActiveSourceHistory()) {
activeSourceEvent.dump(pw, sdf);
}
pw.decreaseIndent();
pw.decreaseIndent();
}
pw.println("CEC message history:");
pw.increaseIndent();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (Dumpable record : mMessageHistory) {
record.dump(pw, sdf);
}

View File

@@ -16,6 +16,7 @@
package com.android.server.hdmi;
import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -471,8 +472,27 @@ abstract class HdmiCecLocalDevice {
return false;
}
@CallSuper
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
return false;
// <Report Physical Address> is also handled in HdmiCecNetwork to update the local network
// state
int address = message.getSource();
// Ignore if [Device Discovery Action] is going on.
if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
return true;
}
HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
// If no non-default display name is available for the device, request the devices OSD name.
if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
}
return true;
}
protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
@@ -708,7 +728,7 @@ abstract class HdmiCecLocalDevice {
}
protected boolean handleSetOsdName(HdmiCecMessage message) {
// The default behavior of <Set Osd Name> is doing nothing.
// <Set OSD name> is also handled in HdmiCecNetwork to update the local network state
return true;
}
@@ -724,7 +744,8 @@ abstract class HdmiCecLocalDevice {
}
protected boolean handleReportPowerStatus(HdmiCecMessage message) {
return false;
// <Report Power Status> is also handled in HdmiCecNetwork to update the local network state
return true;
}
protected boolean handleTimerStatus(HdmiCecMessage message) {

View File

@@ -37,7 +37,6 @@ import android.os.SystemProperties;
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,15 +53,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
/**
* Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
* system.
@@ -104,14 +100,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@GuardedBy("mLock")
private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>();
// Copy of mDeviceInfos to guarantee thread-safety.
@GuardedBy("mLock")
private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
// Map-like container of all cec devices.
// device id is used as key of container.
private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
// Message buffer used to buffer selected messages to process later. <Active Source>
// from a source device, for instance, needs to be buffered if the device is not
// discovered yet. The buffered commands are taken out and when they are ready to
@@ -187,135 +175,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return info != null;
}
/**
* Called when a device is newly added or a new device is detected or
* an existing device is updated.
*
* @param info device info of a new device.
*/
@ServiceThreadOnly
final void addCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
// The addition of the device itself should not be notified.
// Note that different logical address could still be the same local device.
return;
}
if (old == null) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
}
/**
* Called when a device is removed or removal of device is detected.
*
* @param address a logical address of a device to be removed
*/
@ServiceThreadOnly
final void removeCecDevice(int address) {
assertRunOnServiceThread();
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
mCecMessageCache.flushMessagesFrom(address);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
/**
* Called when a device is updated.
*
* @param info device info of the updating device.
*/
@ServiceThreadOnly
final void updateCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
if (old == null) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
}
/**
* Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
* logical address as new device info's.
*
* @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
* @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
* that has the same logical address as new one has.
*/
@ServiceThreadOnly
@VisibleForTesting
protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
assertRunOnServiceThread();
mService.checkLogicalAddressConflictAndReallocate(deviceInfo.getLogicalAddress());
HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
removeDeviceInfo(deviceInfo.getId());
}
mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
updateSafeDeviceInfoList();
return oldDeviceInfo;
}
/**
* Remove a device info corresponding to the given {@code logicalAddress}.
* It returns removed {@link HdmiDeviceInfo} if exists.
*
* @param id id of device to be removed
* @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
*/
@ServiceThreadOnly
private HdmiDeviceInfo removeDeviceInfo(int id) {
assertRunOnServiceThread();
HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
if (deviceInfo != null) {
mDeviceInfos.remove(id);
}
updateSafeDeviceInfoList();
return deviceInfo;
}
/**
* Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
*
* @param logicalAddress logical address of the device to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
@ServiceThreadOnly
HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
}
@ServiceThreadOnly
private void updateSafeDeviceInfoList() {
assertRunOnServiceThread();
List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
synchronized (mLock) {
mSafeAllDeviceInfos = copiedDevices;
}
}
@GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
infoList.add(info);
}
return infoList;
}
private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
mService.invokeDeviceEventListeners(info, status);
}
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
@@ -342,7 +201,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
// Update with TIF on the device removal. TIF callback will update
// mPortIdToTvInputs and mPortIdToTvInputs.
removeCecDevice(info.getLogicalAddress());
mService.getHdmiCecNetwork().removeCecDevice(this, info.getLogicalAddress());
}
}
@@ -399,7 +258,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
boolean lastSystemAudioControlStatus =
SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
clearDeviceInfoList();
mService.getHdmiCecNetwork().clearDeviceList();
launchDeviceDiscovery();
startQueuedActions();
}
@@ -458,7 +317,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// If the new Active Source is under the current device, check if the device info and the TV
// input is ready to switch to the new Active Source. If not ready, buffer the cec command
// to handle later when the device is ready.
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress);
if (info == null) {
HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
mDelayedMessageBuffer.add(message);
@@ -472,79 +331,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return true;
}
@Override
@ServiceThreadOnly
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
assertRunOnServiceThread();
int path = HdmiUtils.twoBytesToInt(message.getParams());
int address = message.getSource();
int type = message.getParams()[2];
// Ignore if [Device Discovery Action] is going on.
if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
return true;
}
// Update the device info with TIF, note that the same device info could have added in
// device discovery and we do not want to override it with default OSD name. Therefore we
// need the following check to skip redundant device info updating.
HdmiDeviceInfo oldDevice = getCecDeviceInfo(address);
if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
addCecDevice(new HdmiDeviceInfo(
address, path, mService.pathToPortId(path), type,
Constants.UNKNOWN_VENDOR_ID, ""));
// if we are adding a new device info, send out a give osd name command
// to update the name of the device in TIF
mService.sendCecCommand(
HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
return true;
}
Slog.w(TAG, "Device info exists. Not updating on Physical Address.");
return true;
}
@Override
protected boolean handleReportPowerStatus(HdmiCecMessage command) {
int newStatus = command.getParams()[0] & 0xFF;
updateDevicePowerStatus(command.getSource(), newStatus);
return true;
}
@Override
@ServiceThreadOnly
protected boolean handleSetOsdName(HdmiCecMessage message) {
int source = message.getSource();
String osdName;
HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
// If the device is not in device list, ignore it.
if (deviceInfo == null) {
Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
return true;
}
try {
osdName = new String(message.getParams(), "US-ASCII");
} catch (UnsupportedEncodingException e) {
Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
return true;
}
if (deviceInfo.getDisplayName() != null
&& deviceInfo.getDisplayName().equals(osdName)) {
Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
return true;
}
Slog.d(TAG, "Updating device OSD name from "
+ deviceInfo.getDisplayName()
+ " to " + osdName);
updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
return true;
}
@Override
@ServiceThreadOnly
protected boolean handleInitiateArc(HdmiCecMessage message) {
@@ -864,14 +650,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
!= HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE) {
return true;
}
boolean isDeviceInCecDeviceList = false;
for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
if (info.getPhysicalAddress() == sourcePhysicalAddress) {
isDeviceInCecDeviceList = true;
break;
}
}
if (!isDeviceInCecDeviceList) {
HdmiDeviceInfo safeDeviceInfoByPath =
mService.getHdmiCecNetwork().getSafeDeviceInfoByPath(sourcePhysicalAddress);
if (safeDeviceInfoByPath == null) {
switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
}
}
@@ -1345,24 +1126,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
routeToInputFromPortId(getRoutingPort());
}
protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
if (info == null) {
Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
return;
}
if (info.getDevicePowerStatus() == newPowerStatus) {
return;
}
HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
// addDeviceInfo replaces old device info with new one if exists.
addDeviceInfo(newInfo);
invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
@@ -1375,27 +1138,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@Override
public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
for (HdmiDeviceInfo info : deviceInfos) {
addCecDevice(info);
mService.getHdmiCecNetwork().addCecDevice(info);
}
}
});
addAndStartAction(action);
}
// Clear all device info.
@ServiceThreadOnly
private void clearDeviceInfoList() {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
if (info.getPhysicalAddress() == mService.getPhysicalAddress()) {
continue;
}
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
mDeviceInfos.clear();
updateSafeDeviceInfoList();
}
@Override
protected void dump(IndentingPrintWriter pw) {
pw.println("HdmiCecLocalDeviceAudioSystem:");
@@ -1409,7 +1158,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
pw.println("mLocalActivePort: " + getLocalActivePort());
HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs);
HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo);
HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos);
pw.decreaseIndent();
super.dump(pw);
}

View File

@@ -42,9 +42,7 @@ import android.media.AudioSystem;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.provider.Settings.Global;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
@@ -53,13 +51,8 @@ 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;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
@@ -95,37 +88,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@GuardedBy("mLock")
private boolean mSystemAudioMute = false;
// Copy of mDeviceInfos to guarantee thread-safety.
@GuardedBy("mLock")
private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
// All external cec input(source) devices. Does not include system audio device.
@GuardedBy("mLock")
private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList();
// Map-like container of all cec devices including local ones.
// device id is used as key of container.
// This is not thread-safe. For external purpose use mSafeDeviceInfos.
private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
// If true, TV going to standby mode puts other devices also to standby.
private boolean mAutoDeviceOff;
// If true, TV wakes itself up when receiving <Text/Image View On>.
private boolean mAutoWakeup;
// List of the logical address of local CEC devices. Unmodifiable, thread-safe.
private List<Integer> mLocalDeviceAddresses;
private final HdmiCecStandbyModeHandler mStandbyHandler;
// If true, do not do routing control/send active source for internal source.
// Set to true when the device was woken up by <Text/Image View On>.
private boolean mSkipRoutingControl;
// Set of physical addresses of CEC switches on the CEC bus. Managed independently from
// other CEC devices since they might not have logical address.
private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>();
// Message buffer used to buffer selected messages to process later. <Active Source>
// from a source device, for instance, needs to be buffered if the device is not
// discovered yet. The buffered commands are taken out and when they are ready to
@@ -205,12 +179,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mAddress, mService.getPhysicalAddress(), mDeviceType));
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
mCecSwitches.add(mService.getPhysicalAddress()); // TV is a CEC switch too.
mService.getHdmiCecNetwork().addCecSwitch(
mService.getHdmiCecNetwork().getPhysicalAddress()); // TV is a CEC switch too.
mTvInputs.clear();
mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE);
launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC &&
reason != HdmiControlService.INITIATED_BY_BOOT_UP);
mLocalDeviceAddresses = initLocalDeviceAddresses();
resetSelectRequestBuffer();
launchDeviceDiscovery();
startQueuedActions();
@@ -219,17 +193,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
@ServiceThreadOnly
private List<Integer> initLocalDeviceAddresses() {
assertRunOnServiceThread();
List<Integer> addresses = new ArrayList<>();
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
addresses.add(device.getDeviceInfo().getLogicalAddress());
}
return Collections.unmodifiableList(addresses);
}
@ServiceThreadOnly
public void setSelectRequestBuffer(SelectRequestBuffer requestBuffer) {
assertRunOnServiceThread();
@@ -272,7 +235,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
void deviceSelect(int id, IHdmiControlCallback callback) {
assertRunOnServiceThread();
HdmiDeviceInfo targetDevice = mDeviceInfos.get(id);
HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(id);
if (targetDevice == null) {
invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
return;
@@ -335,7 +298,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
setActiveSource(newActive, caller);
int logicalAddress = newActive.logicalAddress;
if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
if (mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress) != null
&& logicalAddress != mAddress) {
if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
setPrevPortId(getActivePortId());
}
@@ -374,7 +338,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Show OSD port change banner
if (notifyInputChange) {
ActiveSource activeSource = getActiveSource();
HdmiDeviceInfo info = getCecDeviceInfo(activeSource.logicalAddress);
HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(
activeSource.logicalAddress);
if (info == null) {
info = mService.getDeviceInfoByPort(getActivePortId());
if (info == null) {
@@ -442,7 +407,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (getActiveSource().isValid()) {
return getActiveSource().logicalAddress;
}
HdmiDeviceInfo info = getDeviceInfoByPath(getActivePath());
HdmiDeviceInfo info = mService.getHdmiCecNetwork().getDeviceInfoByPath(getActivePath());
if (info != null) {
return info.getLogicalAddress();
}
@@ -455,7 +420,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
assertRunOnServiceThread();
int logicalAddress = message.getSource();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
HdmiDeviceInfo info = mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress);
if (info == null) {
if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) {
HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
@@ -463,7 +428,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
} else if (isInputReady(info.getId())
|| info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
HdmiControlManager.POWER_STATUS_ON);
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
} else {
@@ -490,7 +456,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (portId != Constants.INVALID_PORT_ID) {
// TODO: Do this only if TV is not showing multiview like PIP/PAP.
HdmiDeviceInfo inactiveSource = getCecDeviceInfo(message.getSource());
HdmiDeviceInfo inactiveSource = mService.getHdmiCecNetwork().getCecDeviceInfo(
message.getSource());
if (inactiveSource == null) {
return true;
}
@@ -546,41 +513,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
@Override
@ServiceThreadOnly
protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
assertRunOnServiceThread();
super.handleReportPhysicalAddress(message);
int path = HdmiUtils.twoBytesToInt(message.getParams());
int address = message.getSource();
int type = message.getParams()[2];
if (updateCecSwitchInfo(address, type, path)) return true;
// Ignore if [Device Discovery Action] is going on.
if (hasAction(DeviceDiscoveryAction.class)) {
Slog.i(TAG, "Ignored while Device Discovery Action is in progress: " + message);
return true;
}
if (!isInDeviceList(address, path)) {
if (!mService.getHdmiCecNetwork().isInDeviceList(address, path)) {
handleNewDeviceAtTheTailOfActivePath(path);
}
// Add the device ahead with default information to handle <Active Source>
// promptly, rather than waiting till the new device action is finished.
HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
addCecDevice(deviceInfo);
startNewDeviceAction(ActiveSource.of(address, path), type);
return true;
}
@Override
protected boolean handleReportPowerStatus(HdmiCecMessage command) {
int newStatus = command.getParams()[0] & 0xFF;
updateDevicePowerStatus(command.getSource(), newStatus);
return true;
}
@Override
protected boolean handleTimerStatus(HdmiCecMessage message) {
// Do nothing.
@@ -593,19 +538,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return true;
}
boolean updateCecSwitchInfo(int address, int type, int path) {
if (address == Constants.ADDR_UNREGISTERED
&& type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
mCecSwitches.add(path);
updateSafeDeviceInfoList();
return true; // Pure switch does not need further processing. Return here.
}
if (type == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
mCecSwitches.add(path);
}
return false;
}
void startNewDeviceAction(ActiveSource activeSource, int deviceType) {
for (NewDeviceAction action : getActions(NewDeviceAction.class)) {
// If there is new device action which has the same logical address and path
@@ -719,35 +651,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return handleTextViewOn(message);
}
@Override
@ServiceThreadOnly
protected boolean handleSetOsdName(HdmiCecMessage message) {
int source = message.getSource();
HdmiDeviceInfo deviceInfo = getCecDeviceInfo(source);
// If the device is not in device list, ignore it.
if (deviceInfo == null) {
Slog.e(TAG, "No source device info for <Set Osd Name>." + message);
return true;
}
String osdName = null;
try {
osdName = new String(message.getParams(), "US-ASCII");
} catch (UnsupportedEncodingException e) {
Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
return true;
}
if (deviceInfo.getDisplayName().equals(osdName)) {
Slog.i(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
return true;
}
addCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName));
return true;
}
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
@@ -757,14 +660,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@Override
public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
for (HdmiDeviceInfo info : deviceInfos) {
addCecDevice(info);
mService.getHdmiCecNetwork().addCecDevice(info);
}
// Since we removed all devices when it's start and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
addCecDevice(device.getDeviceInfo());
mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
}
mSelectRequestBuffer.process();
@@ -798,11 +701,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
private void clearDeviceInfoList() {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : mSafeExternalInputs) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
mDeviceInfos.clear();
updateSafeDeviceInfoList();
mService.getHdmiCecNetwork().clearDeviceList();
}
@ServiceThreadOnly
@@ -1224,170 +1123,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
&& getAvrDeviceInfo() != null;
}
/**
* Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
* logical address as new device info's.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
* @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
* that has the same logical address as new one has.
*/
@ServiceThreadOnly
private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
assertRunOnServiceThread();
HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
if (oldDeviceInfo != null) {
removeDeviceInfo(deviceInfo.getId());
}
mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
updateSafeDeviceInfoList();
return oldDeviceInfo;
}
/**
* Remove a device info corresponding to the given {@code logicalAddress}.
* It returns removed {@link HdmiDeviceInfo} if exists.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param id id of device to be removed
* @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
*/
@ServiceThreadOnly
private HdmiDeviceInfo removeDeviceInfo(int id) {
assertRunOnServiceThread();
HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
if (deviceInfo != null) {
mDeviceInfos.remove(id);
}
updateSafeDeviceInfoList();
return deviceInfo;
}
/**
* Return a list of all {@link HdmiDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
* This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which
* does not include local device.
*/
@ServiceThreadOnly
List<HdmiDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
assertRunOnServiceThread();
if (includeLocalDevice) {
return HdmiUtils.sparseArrayToList(mDeviceInfos);
} else {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); ++i) {
HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
if (!isLocalDeviceAddress(info.getLogicalAddress())) {
infoList.add(info);
}
}
return infoList;
}
}
/**
* Return external input devices.
*/
@GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
return mSafeExternalInputs;
}
@ServiceThreadOnly
private void updateSafeDeviceInfoList() {
assertRunOnServiceThread();
List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
List<HdmiDeviceInfo> externalInputs = getInputDevices();
synchronized (mLock) {
mSafeAllDeviceInfos = copiedDevices;
mSafeExternalInputs = externalInputs;
}
}
/**
* Return a list of external cec input (source) devices.
*
* <p>Note that this effectively excludes non-source devices like system audio,
* secondary TV.
*/
private List<HdmiDeviceInfo> getInputDevices() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); ++i) {
HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
if (isLocalDeviceAddress(info.getLogicalAddress())) {
continue;
}
if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) {
infoList.add(info);
}
}
return infoList;
}
// Check if we are hiding CEC devices connected to a legacy (non-CEC) switch.
// 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.
private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) {
return HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH
&& !isConnectedToCecSwitch(info.getPhysicalAddress(), mCecSwitches);
}
private static boolean isConnectedToCecSwitch(int path, Collection<Integer> switches) {
for (int switchPath : switches) {
if (isParentPath(switchPath, path)) {
return true;
}
}
return false;
}
private static boolean isParentPath(int parentPath, int childPath) {
// (A000, AB00) (AB00, ABC0), (ABC0, ABCD)
// If child's last non-zero nibble is removed, the result equals to the parent.
for (int i = 0; i <= 12; i += 4) {
int nibble = (childPath >> i) & 0xF;
if (nibble != 0) {
int parentNibble = (parentPath >> i) & 0xF;
return parentNibble == 0 && (childPath >> i+4) == (parentPath >> i+4);
}
}
return false;
}
private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
if (!hideDevicesBehindLegacySwitch(info)) {
mService.invokeDeviceEventListeners(info, status);
}
}
private boolean isLocalDeviceAddress(int address) {
return mLocalDeviceAddresses.contains(address);
}
@ServiceThreadOnly
HdmiDeviceInfo getAvrDeviceInfo() {
assertRunOnServiceThread();
return getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
/**
* Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
*
* This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
*
* @param logicalAddress logical address of the device to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
@ServiceThreadOnly
HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
return mService.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
boolean hasSystemAudioDevice() {
@@ -1395,74 +1134,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
HdmiDeviceInfo getSafeAvrDeviceInfo() {
return getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
/**
* Thread safe version of {@link #getCecDeviceInfo(int)}.
*
* @param logicalAddress logical address to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
synchronized (mLock) {
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
return info;
}
}
return null;
}
}
@GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (isLocalDeviceAddress(info.getLogicalAddress())) {
continue;
}
infoList.add(info);
}
return infoList;
}
/**
* Called when a device is newly added or a new device is detected or
* existing device is updated.
*
* @param info device info of a new device.
*/
@ServiceThreadOnly
final void addCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
if (info.getLogicalAddress() == mAddress) {
// The addition of TV device itself should not be notified.
return;
}
if (old == null) {
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(old, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
}
/**
* Called when a device is removed or removal of device is detected.
*
* @param address a logical address of a device to be removed
*/
@ServiceThreadOnly
final void removeCecDevice(int address) {
assertRunOnServiceThread();
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
mCecMessageCache.flushMessagesFrom(address);
invokeDeviceEventListener(info, HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
@ServiceThreadOnly
void handleRemoveActiveRoutingPath(int path) {
@@ -1501,72 +1175,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
/**
* Returns the {@link HdmiDeviceInfo} instance whose physical address matches
* the given routing path. CEC devices use routing path for its physical address to
* describe the hierarchy of the devices in the network.
*
* @param path routing path or physical address
* @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
*/
@ServiceThreadOnly
final HdmiDeviceInfo getDeviceInfoByPath(int path) {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : getDeviceInfoList(false)) {
if (info.getPhysicalAddress() == path) {
return info;
}
}
return null;
}
/**
* Returns the {@link HdmiDeviceInfo} instance whose physical address matches
* the given routing path. This is the version accessible safely from threads
* other than service thread.
*
* @param path routing path or physical address
* @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
*/
HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
synchronized (mLock) {
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (info.getPhysicalAddress() == path) {
return info;
}
}
return null;
}
}
/**
* Whether a device of the specified physical address and logical address exists
* in a device info list. However, both are minimal condition and it could
* be different device from the original one.
*
* @param logicalAddress logical address of a device to be searched
* @param physicalAddress physical address of a device to be searched
* @return true if exist; otherwise false
*/
@ServiceThreadOnly
boolean isInDeviceList(int logicalAddress, int physicalAddress) {
assertRunOnServiceThread();
HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
if (device == null) {
return false;
}
return device.getPhysicalAddress() == physicalAddress;
}
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
if (!connected) {
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.
@@ -1588,16 +1200,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
private void removeCecSwitches(int portId) {
Iterator<Integer> it = mCecSwitches.iterator();
while (!it.hasNext()) {
int path = it.next();
if (pathToPortId(path) == portId) {
it.remove();
}
}
}
@Override
@ServiceThreadOnly
void setAutoDeviceOff(boolean enabled) {
@@ -1765,7 +1367,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
private boolean checkRecorder(int recorderAddress) {
HdmiDeviceInfo device = getCecDeviceInfo(recorderAddress);
HdmiDeviceInfo device = mService.getHdmiCecNetwork().getCecDeviceInfo(recorderAddress);
return (device != null)
&& (HdmiUtils.getTypeFromAddress(recorderAddress)
== HdmiDeviceInfo.DEVICE_RECORDER);
@@ -1871,24 +1473,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
});
}
void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
if (info == null) {
Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
return;
}
if (info.getDevicePowerStatus() == newPowerStatus) {
return;
}
HdmiDeviceInfo newInfo = HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus);
// addDeviceInfo replaces old device info with new one if exists.
addDeviceInfo(newInfo);
invokeDeviceEventListener(newInfo, HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
@Override
protected boolean handleMenuStatus(HdmiCecMessage message) {
// Do nothing and just return true not to prevent from responding <Feature Abort>.
@@ -1897,7 +1481,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@Override
protected void sendStandby(int deviceId) {
HdmiDeviceInfo targetDevice = mDeviceInfos.get(deviceId);
HdmiDeviceInfo targetDevice = mService.getHdmiCecNetwork().getDeviceInfo(deviceId);
if (targetDevice == null) {
return;
}
@@ -1934,11 +1518,5 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
pw.println("mAutoWakeup: " + mAutoWakeup);
pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
pw.println("mPrevPortId: " + mPrevPortId);
pw.println("CEC devices:");
pw.increaseIndent();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
pw.println(info);
}
pw.decreaseIndent();
}
}

View File

@@ -0,0 +1,822 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.hdmi;
import static com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
import android.os.Looper;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
* Holds information about the current state of the HDMI CEC network. It is the sole source of
* truth for device information in the CEC network.
*
* This information includes:
* - All local devices
* - All HDMI ports, their capabilities and status
* - All devices connected to the CEC bus
*
* This class receives all incoming CEC messages and passively listens to device updates to fill
* out the above information.
* This class should not take any active action in sending CEC messages.
*
* Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD
* names, power states can be outdated.
*/
class HdmiCecNetwork {
private static final String TAG = "HdmiCecNetwork";
protected final Object mLock;
private final HdmiControlService mHdmiControlService;
private final HdmiCecController mHdmiCecController;
private final HdmiMhlControllerStub mHdmiMhlController;
private final Handler mHandler;
// Stores the local CEC devices in the system. Device type is used for key.
private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
// Map-like container of all cec devices including local ones.
// device id is used as key of container.
// This is not thread-safe. For external purpose use mSafeDeviceInfos.
private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
// Set of physical addresses of CEC switches on the CEC bus. Managed independently from
// other CEC devices since they might not have logical address.
private final ArraySet<Integer> mCecSwitches = new ArraySet<>();
// Copy of mDeviceInfos to guarantee thread-safety.
@GuardedBy("mLock")
private List<HdmiDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
// All external cec input(source) devices. Does not include system audio device.
@GuardedBy("mLock")
private List<HdmiDeviceInfo> mSafeExternalInputs = Collections.emptyList();
// HDMI port information. Stored in the unmodifiable list to keep the static information
// from being modified.
@GuardedBy("mLock")
private List<HdmiPortInfo> mPortInfo = Collections.emptyList();
// Map from path(physical address) to port ID.
private UnmodifiableSparseIntArray mPortIdMap;
// Map from port ID to HdmiPortInfo.
private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
// Map from port ID to HdmiDeviceInfo.
private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
HdmiCecNetwork(HdmiControlService hdmiControlService,
HdmiCecController hdmiCecController,
HdmiMhlControllerStub hdmiMhlController) {
mHdmiControlService = hdmiControlService;
mHdmiCecController = hdmiCecController;
mHdmiMhlController = hdmiMhlController;
mHandler = new Handler(mHdmiControlService.getServiceLooper());
mLock = mHdmiControlService.getServiceLock();
}
private static boolean isConnectedToCecSwitch(int path, Collection<Integer> switches) {
for (int switchPath : switches) {
if (isParentPath(switchPath, path)) {
return true;
}
}
return false;
}
private static boolean isParentPath(int parentPath, int childPath) {
// (A000, AB00) (AB00, ABC0), (ABC0, ABCD)
// If child's last non-zero nibble is removed, the result equals to the parent.
for (int i = 0; i <= 12; i += 4) {
int nibble = (childPath >> i) & 0xF;
if (nibble != 0) {
int parentNibble = (parentPath >> i) & 0xF;
return parentNibble == 0 && (childPath >> i + 4) == (parentPath >> i + 4);
}
}
return false;
}
public void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
mLocalDevices.put(deviceType, device);
}
/**
* Return the locally hosted logical device of a given type.
*
* @param deviceType logical device type
* @return {@link HdmiCecLocalDevice} instance if the instance of the type is available;
* otherwise null.
*/
HdmiCecLocalDevice getLocalDevice(int deviceType) {
return mLocalDevices.get(deviceType);
}
/**
* Return a list of all {@link HdmiCecLocalDevice}s.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
@ServiceThreadOnly
List<HdmiCecLocalDevice> getLocalDeviceList() {
assertRunOnServiceThread();
return HdmiUtils.sparseArrayToList(mLocalDevices);
}
@ServiceThreadOnly
boolean isAllocatedLocalDeviceAddress(int address) {
assertRunOnServiceThread();
for (int i = 0; i < mLocalDevices.size(); ++i) {
if (mLocalDevices.valueAt(i).isAddressOf(address)) {
return true;
}
}
return false;
}
/**
* Clear all logical addresses registered in the device.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*/
@ServiceThreadOnly
void clearLogicalAddress() {
assertRunOnServiceThread();
for (int i = 0; i < mLocalDevices.size(); ++i) {
mLocalDevices.valueAt(i).clearAddress();
}
}
@ServiceThreadOnly
void clearLocalDevices() {
assertRunOnServiceThread();
mLocalDevices.clear();
}
public HdmiDeviceInfo getDeviceInfo(int id) {
return mDeviceInfos.get(id);
}
/**
* Add a new {@link HdmiDeviceInfo}. It returns old device info which has the same
* logical address as new device info's.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param deviceInfo a new {@link HdmiDeviceInfo} to be added.
* @return {@code null} if it is new device. Otherwise, returns old {@HdmiDeviceInfo}
* that has the same logical address as new one has.
*/
@ServiceThreadOnly
private HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
assertRunOnServiceThread();
HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
mHdmiControlService.checkLogicalAddressConflictAndReallocate(
deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress());
if (oldDeviceInfo != null) {
removeDeviceInfo(deviceInfo.getId());
}
mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
updateSafeDeviceInfoList();
return oldDeviceInfo;
}
/**
* Remove a device info corresponding to the given {@code logicalAddress}.
* It returns removed {@link HdmiDeviceInfo} if exists.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param id id of device to be removed
* @return removed {@link HdmiDeviceInfo} it exists. Otherwise, returns {@code null}
*/
@ServiceThreadOnly
private HdmiDeviceInfo removeDeviceInfo(int id) {
assertRunOnServiceThread();
HdmiDeviceInfo deviceInfo = mDeviceInfos.get(id);
if (deviceInfo != null) {
mDeviceInfos.remove(id);
}
updateSafeDeviceInfoList();
return deviceInfo;
}
/**
* Return a {@link HdmiDeviceInfo} corresponding to the given {@code logicalAddress}.
*
* This is not thread-safe. For thread safety, call {@link #getSafeCecDeviceInfo(int)}.
*
* @param logicalAddress logical address of the device to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
@ServiceThreadOnly
HdmiDeviceInfo getCecDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
return mDeviceInfos.get(HdmiDeviceInfo.idForCecDevice(logicalAddress));
}
/**
* Called when a device is newly added or a new device is detected or
* existing device is updated.
*
* @param info device info of a new device.
*/
@ServiceThreadOnly
final void addCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
if (isLocalDeviceAddress(info.getLogicalAddress())) {
// The addition of a local device should not notify listeners
return;
}
if (old == null) {
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(old,
HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
}
private void invokeDeviceEventListener(HdmiDeviceInfo info, int event) {
if (!hideDevicesBehindLegacySwitch(info)) {
mHdmiControlService.invokeDeviceEventListeners(info, event);
}
}
/**
* Called when a device is updated.
*
* @param info device info of the updating device.
*/
@ServiceThreadOnly
final void updateCecDevice(HdmiDeviceInfo info) {
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
if (old == null) {
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
}
@ServiceThreadOnly
private void updateSafeDeviceInfoList() {
assertRunOnServiceThread();
List<HdmiDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
List<HdmiDeviceInfo> externalInputs = getInputDevices();
mSafeAllDeviceInfos = copiedDevices;
mSafeExternalInputs = externalInputs;
}
/**
* Return a list of all {@link HdmiDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
* This is not thread-safe. For thread safety, call {@link #getSafeExternalInputsLocked} which
* does not include local device.
*/
@ServiceThreadOnly
List<HdmiDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
assertRunOnServiceThread();
if (includeLocalDevice) {
return HdmiUtils.sparseArrayToList(mDeviceInfos);
} else {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); ++i) {
HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
if (!isLocalDeviceAddress(info.getLogicalAddress())) {
infoList.add(info);
}
}
return infoList;
}
}
/**
* Return external input devices.
*/
@GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
return mSafeExternalInputs;
}
/**
* Return a list of external cec input (source) devices.
*
* <p>Note that this effectively excludes non-source devices like system audio,
* secondary TV.
*/
private List<HdmiDeviceInfo> getInputDevices() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); ++i) {
HdmiDeviceInfo info = mDeviceInfos.valueAt(i);
if (isLocalDeviceAddress(info.getLogicalAddress())) {
continue;
}
if (info.isSourceType() && !hideDevicesBehindLegacySwitch(info)) {
infoList.add(info);
}
}
return infoList;
}
// Check if we are hiding CEC devices connected to a legacy (non-CEC) switch.
// 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.
private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) {
return isLocalDeviceAddress(Constants.ADDR_TV)
&& HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH
&& !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches());
}
/**
* Called when a device is removed or removal of device is detected.
*
* @param address a logical address of a device to be removed
*/
@ServiceThreadOnly
final void removeCecDevice(HdmiCecLocalDevice localDevice, int address) {
assertRunOnServiceThread();
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
localDevice.mCecMessageCache.flushMessagesFrom(address);
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
public void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
if (info == null) {
Slog.w(TAG, "Can not update power status of non-existing device:" + logicalAddress);
return;
}
if (info.getDevicePowerStatus() == newPowerStatus) {
return;
}
updateCecDevice(HdmiUtils.cloneHdmiDeviceInfo(info, newPowerStatus));
}
/**
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
int portId = physicalAddressToPortId(physicalAddress);
if (portId != Constants.INVALID_PORT_ID) {
return mPortInfoMap.get(portId).isArcSupported();
}
return false;
}
// Initialize HDMI port information. Combine the information from CEC and MHL HAL and
// keep them in one place.
@ServiceThreadOnly
@VisibleForTesting
public void initPortInfo() {
assertRunOnServiceThread();
HdmiPortInfo[] cecPortInfo = null;
// CEC HAL provides majority of the info while MHL does only MHL support flag for
// each port. Return empty array if CEC HAL didn't provide the info.
if (mHdmiCecController != null) {
cecPortInfo = mHdmiCecController.getPortInfos();
}
if (cecPortInfo == null) {
return;
}
SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
SparseIntArray portIdMap = new SparseIntArray();
SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
for (HdmiPortInfo info : cecPortInfo) {
portIdMap.put(info.getAddress(), info.getId());
portInfoMap.put(info.getId(), info);
portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
}
mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
if (mHdmiMhlController == null) {
return;
}
HdmiPortInfo[] mhlPortInfo = mHdmiMhlController.getPortInfos();
ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
for (HdmiPortInfo info : mhlPortInfo) {
if (info.isMhlSupported()) {
mhlSupportedPorts.add(info.getId());
}
}
// Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
// cec port info if we do not have have port that supports MHL.
if (mhlSupportedPorts.isEmpty()) {
setPortInfo(Collections.unmodifiableList(Arrays.asList(cecPortInfo)));
return;
}
ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
for (HdmiPortInfo info : cecPortInfo) {
if (mhlSupportedPorts.contains(info.getId())) {
result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
info.isCecSupported(), true, info.isArcSupported()));
} else {
result.add(info);
}
}
setPortInfo(Collections.unmodifiableList(result));
}
HdmiDeviceInfo getDeviceForPortId(int portId) {
return mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
}
/**
* Whether a device of the specified physical address and logical address exists
* in a device info list. However, both are minimal condition and it could
* be different device from the original one.
*
* @param logicalAddress logical address of a device to be searched
* @param physicalAddress physical address of a device to be searched
* @return true if exist; otherwise false
*/
@ServiceThreadOnly
boolean isInDeviceList(int logicalAddress, int physicalAddress) {
assertRunOnServiceThread();
HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
if (device == null) {
return false;
}
return device.getPhysicalAddress() == physicalAddress;
}
/**
* Passively listen to incoming CEC messages.
*
* This shall not result in any CEC messages being sent.
*/
@ServiceThreadOnly
public void handleCecMessage(HdmiCecMessage message) {
assertRunOnServiceThread();
// Add device by logical address if it's not already known
int sourceAddress = message.getSource();
if (getCecDeviceInfo(sourceAddress) == null) {
HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress,
HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID,
HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID,
HdmiUtils.getDefaultDeviceName(sourceAddress));
addCecDevice(newDevice);
}
switch (message.getOpcode()) {
case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
handleReportPhysicalAddress(message);
break;
case Constants.MESSAGE_REPORT_POWER_STATUS:
handleReportPowerStatus(message);
break;
case Constants.MESSAGE_SET_OSD_NAME:
handleSetOsdName(message);
break;
}
}
@ServiceThreadOnly
private void handleReportPhysicalAddress(HdmiCecMessage message) {
assertRunOnServiceThread();
int logicalAddress = message.getSource();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
int type = message.getParams()[2];
if (updateCecSwitchInfo(logicalAddress, type, physicalAddress)) return;
HdmiDeviceInfo deviceInfo = getCecDeviceInfo(logicalAddress);
if (deviceInfo == null) {
Slog.i(TAG, "Unknown source device info for <Report Physical Address> " + message);
} else {
HdmiDeviceInfo updatedDeviceInfo = new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
physicalAddress,
physicalAddressToPortId(physicalAddress), type, deviceInfo.getVendorId(),
deviceInfo.getDisplayName(), deviceInfo.getDevicePowerStatus());
updateCecDevice(updatedDeviceInfo);
}
}
@ServiceThreadOnly
private void handleReportPowerStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
// Update power status of device
int newStatus = message.getParams()[0] & 0xFF;
updateDevicePowerStatus(message.getSource(), newStatus);
}
@ServiceThreadOnly
private void handleSetOsdName(HdmiCecMessage message) {
assertRunOnServiceThread();
int logicalAddress = message.getSource();
String osdName;
HdmiDeviceInfo deviceInfo = getCecDeviceInfo(logicalAddress);
// If the device is not in device list, ignore it.
if (deviceInfo == null) {
Slog.i(TAG, "No source device info for <Set Osd Name>." + message);
return;
}
try {
osdName = new String(message.getParams(), "US-ASCII");
} catch (UnsupportedEncodingException e) {
Slog.e(TAG, "Invalid <Set Osd Name> request:" + message, e);
return;
}
if (deviceInfo.getDisplayName() != null
&& deviceInfo.getDisplayName().equals(osdName)) {
Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
return;
}
Slog.d(TAG, "Updating device OSD name from "
+ deviceInfo.getDisplayName()
+ " to " + osdName);
updateCecDevice(new HdmiDeviceInfo(deviceInfo.getLogicalAddress(),
deviceInfo.getPhysicalAddress(), deviceInfo.getPortId(),
deviceInfo.getDeviceType(), deviceInfo.getVendorId(), osdName,
deviceInfo.getDevicePowerStatus()));
}
void addCecSwitch(int physicalAddress) {
mCecSwitches.add(physicalAddress);
}
public ArraySet<Integer> getCecSwitches() {
return mCecSwitches;
}
void removeDevicesConnectedToPort(int portId) {
Iterator<Integer> it = mCecSwitches.iterator();
while (it.hasNext()) {
int path = it.next();
int devicePortId = physicalAddressToPortId(path);
if (devicePortId == portId || devicePortId == Constants.INVALID_PORT_ID) {
it.remove();
}
}
List<Integer> toRemove = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); i++) {
int key = mDeviceInfos.keyAt(i);
int physicalAddress = mDeviceInfos.get(key).getPhysicalAddress();
int devicePortId = physicalAddressToPortId(physicalAddress);
if (devicePortId == portId || devicePortId == Constants.INVALID_PORT_ID) {
toRemove.add(key);
}
}
for (Integer key : toRemove) {
removeDeviceInfo(key);
}
}
boolean updateCecSwitchInfo(int address, int type, int path) {
if (address == Constants.ADDR_UNREGISTERED
&& type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
mCecSwitches.add(path);
updateSafeDeviceInfoList();
return true; // Pure switch does not need further processing. Return here.
}
if (type == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
mCecSwitches.add(path);
}
return false;
}
@GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (isLocalDeviceAddress(info.getLogicalAddress())) {
continue;
}
infoList.add(info);
}
return infoList;
}
/**
* Thread safe version of {@link #getCecDeviceInfo(int)}.
*
* @param logicalAddress logical address to be retrieved
* @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
* Returns null if no logical address matched
*/
HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
return info;
}
}
return null;
}
/**
* Returns the {@link HdmiDeviceInfo} instance whose physical address matches
*
*
*
* qq * the given routing path. CEC devices use routing path for its physical address to
* describe the hierarchy of the devices in the network.
*
* @param path routing path or physical address
* @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
*/
@ServiceThreadOnly
final HdmiDeviceInfo getDeviceInfoByPath(int path) {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : getDeviceInfoList(false)) {
if (info.getPhysicalAddress() == path) {
return info;
}
}
return null;
}
/**
* Returns the {@link HdmiDeviceInfo} instance whose physical address matches
* the given routing path. This is the version accessible safely from threads
* other than service thread.
*
* @param path routing path or physical address
* @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
*/
HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
if (info.getPhysicalAddress() == path) {
return info;
}
}
return null;
}
public int getPhysicalAddress() {
return mHdmiCecController.getPhysicalAddress();
}
@ServiceThreadOnly
public void clear() {
assertRunOnServiceThread();
initPortInfo();
clearDeviceList();
clearLocalDevices();
}
@ServiceThreadOnly
public void clearDeviceList() {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
if (info.getPhysicalAddress() == getPhysicalAddress()) {
continue;
}
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
mDeviceInfos.clear();
updateSafeDeviceInfoList();
}
/**
* Returns HDMI port information for the given port id.
*
* @param portId HDMI port id
* @return {@link HdmiPortInfo} for the given port
*/
HdmiPortInfo getPortInfo(int portId) {
return mPortInfoMap.get(portId, null);
}
/**
* Returns the routing path (physical address) of the HDMI port for the given
* port id.
*/
int portIdToPath(int portId) {
HdmiPortInfo portInfo = getPortInfo(portId);
if (portInfo == null) {
Slog.e(TAG, "Cannot find the port info: " + portId);
return Constants.INVALID_PHYSICAL_ADDRESS;
}
return portInfo.getAddress();
}
/**
* Returns the id of HDMI port located at the current device that runs this method.
*
* For TV with physical address 0x0000, target device 0x1120, we want port physical address
* 0x1000 to get the correct port id from {@link #mPortIdMap}. For device with Physical Address
* 0x2000, target device 0x2420, we want port address 0x24000 to get the port id.
*
* <p>Return {@link Constants#INVALID_PORT_ID} if target device does not connect to.
*
* @param path the target device's physical address.
* @return the id of the port that the target device eventually connects to
* on the current device.
*/
int physicalAddressToPortId(int path) {
int mask = 0xF000;
int finalMask = 0xF000;
int physicalAddress;
physicalAddress = getPhysicalAddress();
int maskedAddress = physicalAddress;
while (maskedAddress != 0) {
maskedAddress = physicalAddress & mask;
finalMask |= mask;
mask >>= 4;
}
int portAddress = path & finalMask;
return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
}
List<HdmiPortInfo> getPortInfo() {
return mPortInfo;
}
void setPortInfo(List<HdmiPortInfo> portInfo) {
mPortInfo = portInfo;
}
private boolean isLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); i++) {
int key = mLocalDevices.keyAt(i);
if (mLocalDevices.get(key).mAddress == address) {
return true;
}
}
return false;
}
private void assertRunOnServiceThread() {
if (Looper.myLooper() != mHandler.getLooper()) {
throw new IllegalStateException("Should run on service thread.");
}
}
protected void dump(IndentingPrintWriter pw) {
pw.println("HDMI CEC Network");
pw.increaseIndent();
HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
for (int i = 0; i < mLocalDevices.size(); ++i) {
pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
pw.increaseIndent();
mLocalDevices.valueAt(i).dump(pw);
pw.println("Active Source history:");
pw.increaseIndent();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ArrayBlockingQueue<HdmiCecController.Dumpable> activeSourceHistory =
mLocalDevices.valueAt(i).getActiveSourceHistory();
for (HdmiCecController.Dumpable activeSourceEvent : activeSourceHistory) {
activeSourceEvent.dump(pw, sdf);
}
pw.decreaseIndent();
pw.decreaseIndent();
}
HdmiUtils.dumpIterable(pw, "mDeviceInfos:", mSafeAllDeviceInfos);
pw.decreaseIndent();
}
}

View File

@@ -71,10 +71,8 @@ import android.os.UserHandle;
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,7 +89,6 @@ import libcore.util.EmptyArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -177,6 +174,8 @@ public class HdmiControlService extends SystemService {
static final int STANDBY_SCREEN_OFF = 0;
static final int STANDBY_SHUTDOWN = 1;
private HdmiCecNetwork mHdmiCecNetwork;
// Logical address of the active source.
@GuardedBy("mLock")
protected final ActiveSource mActiveSource = new ActiveSource();
@@ -333,21 +332,6 @@ public class HdmiControlService extends SystemService {
@Nullable
private HdmiCecController mCecController;
// HDMI port information. Stored in the unmodifiable list to keep the static information
// from being modified.
// This variable is null if the current device does not have hdmi input.
@GuardedBy("mLock")
private List<HdmiPortInfo> mPortInfo = null;
// Map from path(physical address) to port ID.
private UnmodifiableSparseIntArray mPortIdMap;
// Map from port ID to HdmiPortInfo.
private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
// Map from port ID to HdmiDeviceInfo.
private UnmodifiableSparseArray<HdmiDeviceInfo> mPortDeviceMap;
private HdmiCecMessageValidator mMessageValidator;
@ServiceThreadOnly
@@ -389,10 +373,6 @@ public class HdmiControlService extends SystemService {
@Nullable
private Looper mIoLooper;
// Thread safe physical address
@GuardedBy("mLock")
private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
// Last input port before switching to the MHL port. Should switch back to this port
// when the mobile device sends the request one touch play with off.
// Gets invalidated if we go to other port/input.
@@ -507,42 +487,7 @@ public class HdmiControlService extends SystemService {
@Override
public void onStart() {
if (mIoLooper == null) {
mIoThread.start();
mIoLooper = mIoThread.getLooper();
}
mPowerStatus = getInitialPowerStatus();
mProhibitMode = false;
mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
mHdmiCecVolumeControlEnabled = readBooleanSetting(
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
if (mCecController == null) {
mCecController = HdmiCecController.create(this, getAtomWriter());
}
if (mCecController != null) {
if (mHdmiControlEnabled) {
initializeCec(INITIATED_BY_BOOT_UP);
} else {
mCecController.setOption(OptionKey.ENABLE_CEC, false);
}
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
return;
}
if (mMhlController == null) {
mMhlController = HdmiMhlControllerStub.create(this);
}
if (!mMhlController.isReady()) {
Slog.i(TAG, "Device does not support MHL-control.");
}
mMhlDevices = Collections.emptyList();
initPortInfo();
if (mMessageValidator == null) {
mMessageValidator = new HdmiCecMessageValidator(this);
}
initService();
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
if (mCecController != null) {
@@ -560,6 +505,46 @@ public class HdmiControlService extends SystemService {
mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
}
@VisibleForTesting
void initService() {
if (mIoLooper == null) {
mIoThread.start();
mIoLooper = mIoThread.getLooper();
}
mPowerStatus = getInitialPowerStatus();
mProhibitMode = false;
mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
mHdmiCecVolumeControlEnabled = readBooleanSetting(
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
if (mCecController == null) {
mCecController = HdmiCecController.create(this, getAtomWriter());
}
if (mCecController == null) {
Slog.i(TAG, "Device does not support HDMI-CEC.");
return;
}
if (mMhlController == null) {
mMhlController = HdmiMhlControllerStub.create(this);
}
if (!mMhlController.isReady()) {
Slog.i(TAG, "Device does not support MHL-control.");
}
mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
if (mHdmiControlEnabled) {
initializeCec(INITIATED_BY_BOOT_UP);
} else {
mCecController.setOption(OptionKey.ENABLE_CEC, false);
}
mMhlDevices = Collections.emptyList();
mHdmiCecNetwork.initPortInfo();
if (mMessageValidator == null) {
mMessageValidator = new HdmiCecMessageValidator(this);
}
}
private void bootCompleted() {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
@@ -587,6 +572,15 @@ public class HdmiControlService extends SystemService {
mCecController = cecController;
}
@VisibleForTesting
void setHdmiCecNetwork(HdmiCecNetwork hdmiCecNetwork) {
mHdmiCecNetwork = hdmiCecNetwork;
}
public HdmiCecNetwork getHdmiCecNetwork() {
return mHdmiCecNetwork;
}
@VisibleForTesting
void setHdmiMhlController(HdmiMhlControllerStub hdmiMhlController) {
mMhlController = hdmiMhlController;
@@ -705,7 +699,7 @@ public class HdmiControlService extends SystemService {
break;
case Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED:
for (int type : mLocalDevices) {
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice != null) {
localDevice.setAutoDeviceOff(enabled);
}
@@ -800,7 +794,7 @@ public class HdmiControlService extends SystemService {
// A container for [Device type, Local device info].
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice == null) {
localDevice = HdmiCecLocalDevice.create(this, type);
}
@@ -832,43 +826,48 @@ public class HdmiControlService extends SystemService {
for (final HdmiCecLocalDevice localDevice : allocatingDevices) {
mCecController.allocateLogicalAddress(localDevice.getType(),
localDevice.getPreferredAddress(), new AllocateAddressCallback() {
@Override
public void onAllocated(int deviceType, int logicalAddress) {
if (logicalAddress == Constants.ADDR_UNREGISTERED) {
Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
} else {
// Set POWER_STATUS_ON to all local devices because they share lifetime
// with system.
HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType,
HdmiControlManager.POWER_STATUS_ON);
localDevice.setDeviceInfo(deviceInfo);
mCecController.addLocalDevice(deviceType, localDevice);
mCecController.addLogicalAddress(logicalAddress);
allocatedDevices.add(localDevice);
}
@Override
public void onAllocated(int deviceType, int logicalAddress) {
if (logicalAddress == Constants.ADDR_UNREGISTERED) {
Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType
+ "]");
} else {
// Set POWER_STATUS_ON to all local devices because they share
// lifetime
// with system.
HdmiDeviceInfo deviceInfo = createDeviceInfo(logicalAddress,
deviceType,
HdmiControlManager.POWER_STATUS_ON);
localDevice.setDeviceInfo(deviceInfo);
mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
mCecController.addLogicalAddress(logicalAddress);
allocatedDevices.add(localDevice);
}
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
mAddressAllocated = true;
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call
// onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
notifyAddressAllocated(allocatedDevices, initiatedBy);
// Reinvoke the saved display status callback once the local
// device is ready.
if (mDisplayStatusCallback != null) {
queryDisplayStatus(mDisplayStatusCallback);
mDisplayStatusCallback = null;
}
if (mOtpCallbackPendingAddressAllocation != null) {
oneTouchPlay(mOtpCallbackPendingAddressAllocation);
mOtpCallbackPendingAddressAllocation = null;
}
mCecMessageBuffer.processMessages();
}
}
notifyAddressAllocated(allocatedDevices, initiatedBy);
// Reinvoke the saved display status callback once the local device is ready.
if (mDisplayStatusCallback != null) {
queryDisplayStatus(mDisplayStatusCallback);
mDisplayStatusCallback = null;
}
if (mOtpCallbackPendingAddressAllocation != null) {
oneTouchPlay(mOtpCallbackPendingAddressAllocation);
mOtpCallbackPendingAddressAllocation = null;
}
mCecMessageBuffer.processMessages();
}
}
});
});
}
}
@@ -888,88 +887,14 @@ public class HdmiControlService extends SystemService {
return mAddressAllocated;
}
// Initialize HDMI port information. Combine the information from CEC and MHL HAL and
// keep them in one place.
@ServiceThreadOnly
@VisibleForTesting
protected void initPortInfo() {
assertRunOnServiceThread();
HdmiPortInfo[] cecPortInfo = null;
synchronized (mLock) {
mPhysicalAddress = getPhysicalAddress();
}
// CEC HAL provides majority of the info while MHL does only MHL support flag for
// each port. Return empty array if CEC HAL didn't provide the info.
if (mCecController != null) {
cecPortInfo = mCecController.getPortInfos();
}
if (cecPortInfo == null) {
return;
}
SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
SparseIntArray portIdMap = new SparseIntArray();
SparseArray<HdmiDeviceInfo> portDeviceMap = new SparseArray<>();
for (HdmiPortInfo info : cecPortInfo) {
portIdMap.put(info.getAddress(), info.getId());
portInfoMap.put(info.getId(), info);
portDeviceMap.put(info.getId(), new HdmiDeviceInfo(info.getAddress(), info.getId()));
}
mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
mPortDeviceMap = new UnmodifiableSparseArray<>(portDeviceMap);
if (mMhlController == null) {
return;
}
HdmiPortInfo[] mhlPortInfo = mMhlController.getPortInfos();
ArraySet<Integer> mhlSupportedPorts = new ArraySet<Integer>(mhlPortInfo.length);
for (HdmiPortInfo info : mhlPortInfo) {
if (info.isMhlSupported()) {
mhlSupportedPorts.add(info.getId());
}
}
// Build HDMI port info list with CEC port info plus MHL supported flag. We can just use
// cec port info if we do not have have port that supports MHL.
if (mhlSupportedPorts.isEmpty()) {
setPortInfo(Collections.unmodifiableList(Arrays.asList(cecPortInfo)));
return;
}
ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
for (HdmiPortInfo info : cecPortInfo) {
if (mhlSupportedPorts.contains(info.getId())) {
result.add(new HdmiPortInfo(info.getId(), info.getType(), info.getAddress(),
info.isCecSupported(), true, info.isArcSupported()));
} else {
result.add(info);
}
}
setPortInfo(Collections.unmodifiableList(result));
}
List<HdmiPortInfo> getPortInfo() {
synchronized (mLock) {
return mPortInfo;
return mHdmiCecNetwork.getPortInfo();
}
}
void setPortInfo(List<HdmiPortInfo> portInfo) {
synchronized (mLock) {
mPortInfo = portInfo;
}
}
/**
* Returns HDMI port information for the given port id.
*
* @param portId HDMI port id
* @return {@link HdmiPortInfo} for the given port
*/
HdmiPortInfo getPortInfo(int portId) {
return mPortInfoMap.get(portId, null);
return mHdmiCecNetwork.getPortInfo(portId);
}
/**
@@ -977,12 +902,7 @@ public class HdmiControlService extends SystemService {
* port id.
*/
int portIdToPath(int portId) {
HdmiPortInfo portInfo = getPortInfo(portId);
if (portInfo == null) {
Slog.e(TAG, "Cannot find the port info: " + portId);
return Constants.INVALID_PHYSICAL_ADDRESS;
}
return portInfo.getAddress();
return mHdmiCecNetwork.portIdToPath(portId);
}
/**
@@ -999,26 +919,11 @@ public class HdmiControlService extends SystemService {
* on the current device.
*/
int pathToPortId(int path) {
int mask = 0xF000;
int finalMask = 0xF000;
int physicalAddress;
synchronized (mLock) {
physicalAddress = mPhysicalAddress;
}
int maskedAddress = physicalAddress;
while (maskedAddress != 0) {
maskedAddress = physicalAddress & mask;
finalMask |= mask;
mask >>= 4;
}
int portAddress = path & finalMask;
return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
return mHdmiCecNetwork.physicalAddressToPortId(path);
}
boolean isValidPortId(int portId) {
return getPortInfo(portId) != null;
return mHdmiCecNetwork.getPortInfo(portId) != null;
}
/**
@@ -1068,7 +973,7 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
assertRunOnServiceThread();
return tv() == null ? null : tv().getCecDeviceInfo(logicalAddress);
return mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
}
@ServiceThreadOnly
@@ -1092,11 +997,7 @@ public class HdmiControlService extends SystemService {
* Whether a device of the specified physical address is connected to ARC enabled port.
*/
boolean isConnectedToArcPort(int physicalAddress) {
int portId = pathToPortId(physicalAddress);
if (portId != Constants.INVALID_PORT_ID) {
return mPortInfoMap.get(portId).isArcSupported();
}
return false;
return mHdmiCecNetwork.isConnectedToArcPort(physicalAddress);
}
@ServiceThreadOnly
@@ -1168,7 +1069,7 @@ public class HdmiControlService extends SystemService {
}
return true;
}
getHdmiCecNetwork().handleCecMessage(message);
if (dispatchMessageToLocalDevice(message)) {
return true;
}
@@ -1183,7 +1084,7 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
assertRunOnServiceThread();
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
if (device.dispatchMessage(message)
&& message.getDestination() != Constants.ADDR_BROADCAST) {
return true;
@@ -1209,12 +1110,12 @@ public class HdmiControlService extends SystemService {
if (connected && !isTvDevice()
&& getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
if (isSwitchDevice()) {
initPortInfo();
mHdmiCecNetwork.initPortInfo();
HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
}
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type);
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
if (localDevice == null) {
localDevice = HdmiCecLocalDevice.create(this, type);
localDevice.init();
@@ -1224,9 +1125,14 @@ public class HdmiControlService extends SystemService {
allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG);
}
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.onHotplug(portId, connected);
}
if (!connected) {
mHdmiCecNetwork.removeDevicesConnectedToPort(portId);
}
announceHotplugEvent(portId, connected);
}
@@ -1262,7 +1168,7 @@ public class HdmiControlService extends SystemService {
List<HdmiCecLocalDevice> getAllLocalDevices() {
assertRunOnServiceThread();
return mCecController.getLocalDeviceList();
return mHdmiCecNetwork.getLocalDeviceList();
}
/**
@@ -1275,8 +1181,14 @@ public class HdmiControlService extends SystemService {
*
* @param logicalAddress logical address of the remote device that might have the same logical
* address as the current device.
* @param physicalAddress physical address of the given device.
*/
protected void checkLogicalAddressConflictAndReallocate(int logicalAddress) {
protected void checkLogicalAddressConflictAndReallocate(int logicalAddress,
int physicalAddress) {
// The given device is a local device. No logical address conflict.
if (physicalAddress == getPhysicalAddress()) {
return;
}
for (HdmiCecLocalDevice device : getAllLocalDevices()) {
if (device.getDeviceInfo().getLogicalAddress() == logicalAddress) {
HdmiLogger.debug("allocate logical address for " + device.getDeviceInfo());
@@ -1616,8 +1528,7 @@ public class HdmiControlService extends SystemService {
return null;
}
if (audioSystem() != null) {
HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) {
for (HdmiDeviceInfo info : mHdmiCecNetwork.getSafeCecDevicesLocked()) {
if (info.getPhysicalAddress() == activeSource.physicalAddress) {
return info;
}
@@ -1649,7 +1560,7 @@ public class HdmiControlService extends SystemService {
}
int activePath = tv.getActivePath();
if (activePath != HdmiDeviceInfo.PATH_INVALID) {
HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
HdmiDeviceInfo info = mHdmiCecNetwork.getSafeDeviceInfoByPath(activePath);
return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
}
return null;
@@ -1752,7 +1663,7 @@ public class HdmiControlService extends SystemService {
return;
}
if (mCecController != null) {
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
if (localDevice == null) {
Slog.w(TAG, "Local device not available to send key event.");
return;
@@ -1774,7 +1685,7 @@ public class HdmiControlService extends SystemService {
Slog.w(TAG, "CEC controller not available to send volume key event.");
return;
}
HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(deviceType);
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(deviceType);
if (localDevice == null) {
Slog.w(TAG, "Local device " + deviceType
+ " not available to send volume key event.");
@@ -1888,7 +1799,7 @@ public class HdmiControlService extends SystemService {
public int getPhysicalAddress() {
enforceAccessPermission();
synchronized (mLock) {
return mPhysicalAddress;
return mHdmiCecNetwork.getPhysicalAddress();
}
}
@@ -1934,13 +1845,8 @@ public class HdmiControlService extends SystemService {
enforceAccessPermission();
// No need to hold the lock for obtaining TV device as the local device instance
// is preserved while the HDMI control is enabled.
HdmiCecLocalDeviceTv tv = tv();
synchronized (mLock) {
List<HdmiDeviceInfo> cecDevices = (tv == null)
? Collections.<HdmiDeviceInfo>emptyList()
: tv.getSafeExternalInputsLocked();
return HdmiUtils.mergeToUnmodifiableList(cecDevices, getMhlDevicesLocked());
}
return HdmiUtils.mergeToUnmodifiableList(mHdmiCecNetwork.getSafeExternalInputsLocked(),
getMhlDevicesLocked());
}
// Returns all the CEC devices on the bus including system audio, switch,
@@ -1948,19 +1854,7 @@ public class HdmiControlService extends SystemService {
@Override
public List<HdmiDeviceInfo> getDeviceList() {
enforceAccessPermission();
HdmiCecLocalDeviceTv tv = tv();
if (tv != null) {
synchronized (mLock) {
return tv.getSafeCecDevicesLocked();
}
} else {
HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
synchronized (mLock) {
return (audioSystem == null)
? Collections.<HdmiDeviceInfo>emptyList()
: audioSystem.getSafeCecDevicesLocked();
}
}
return mHdmiCecNetwork.getSafeCecDevicesLocked();
}
@Override
@@ -2089,7 +1983,7 @@ public class HdmiControlService extends SystemService {
runOnServiceThread(new Runnable() {
@Override
public void run() {
HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
Slog.w(TAG, "Local device not available");
return;
@@ -2117,7 +2011,7 @@ public class HdmiControlService extends SystemService {
mhlDevice.sendStandby();
return;
}
HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
device = audioSystem();
}
@@ -2262,7 +2156,7 @@ public class HdmiControlService extends SystemService {
runOnServiceThread(new Runnable() {
@Override
public void run() {
HdmiCecLocalDevice device = mCecController.getLocalDevice(deviceType);
HdmiCecLocalDevice device = mHdmiCecNetwork.getLocalDevice(deviceType);
if (device == null) {
Slog.w(TAG, "Local device not available");
return;
@@ -2339,8 +2233,7 @@ public class HdmiControlService extends SystemService {
pw.increaseIndent();
mMhlController.dump(pw);
pw.decreaseIndent();
HdmiUtils.dumpIterable(pw, "mPortInfo:", mPortInfo);
mHdmiCecNetwork.dump(pw);
if (mCecController != null) {
pw.println("mCecController: ");
pw.increaseIndent();
@@ -2832,7 +2725,7 @@ public class HdmiControlService extends SystemService {
}
public HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
return (HdmiCecLocalDeviceTv) mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
boolean isTvDevice() {
@@ -2857,11 +2750,11 @@ public class HdmiControlService extends SystemService {
protected HdmiCecLocalDevicePlayback playback() {
return (HdmiCecLocalDevicePlayback)
mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK);
}
public HdmiCecLocalDeviceAudioSystem audioSystem() {
return (HdmiCecLocalDeviceAudioSystem) mCecController.getLocalDevice(
return (HdmiCecLocalDeviceAudioSystem) mHdmiCecNetwork.getLocalDevice(
HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
}
@@ -2991,7 +2884,7 @@ public class HdmiControlService extends SystemService {
}
private boolean canGoToStandby() {
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
if (!device.canGoToStandby()) return false;
}
return true;
@@ -3025,7 +2918,7 @@ public class HdmiControlService extends SystemService {
private void disableDevices(PendingActionClearedCallback callback) {
if (mCecController != null) {
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.disableDevice(mStandbyMessageReceived, callback);
}
}
@@ -3039,7 +2932,8 @@ public class HdmiControlService extends SystemService {
return;
}
mCecController.clearLogicalAddress();
mCecController.clearLocalDevices();
mHdmiCecNetwork.clearLogicalAddress();
mHdmiCecNetwork.clearLocalDevices();
}
@ServiceThreadOnly
@@ -3051,7 +2945,7 @@ public class HdmiControlService extends SystemService {
return;
}
mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
device.onStandby(mStandbyMessageReceived, standbyAction);
}
mStandbyMessageReceived = false;
@@ -3411,7 +3305,7 @@ public class HdmiControlService extends SystemService {
// input change listener should be the one describing the corresponding HDMI port.
HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId);
HdmiDeviceInfo info = (device != null) ? device.getInfo()
: mPortDeviceMap.get(portId, HdmiDeviceInfo.INACTIVE_DEVICE);
: mHdmiCecNetwork.getDeviceForPortId(portId);
invokeInputChangeListener(info);
}

View File

@@ -148,7 +148,8 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
BitSet currentInfos = infoListToBitSet(tv().getDeviceInfoList(false), audioOnly);
BitSet currentInfos = infoListToBitSet(
localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false), audioOnly);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
@@ -225,11 +226,11 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
mayCancelOneTouchRecord(removedAddress);
mayDisableSystemAudioAndARC(removedAddress);
tv().removeCecDevice(removedAddress);
localDevice().mService.getHdmiCecNetwork().removeCecDevice(localDevice(), removedAddress);
}
private void mayChangeRoutingPath(int address) {
HdmiDeviceInfo info = tv().getCecDeviceInfo(address);
HdmiDeviceInfo info = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo(address);
if (info != null) {
tv().handleRemoveActiveRoutingPath(info.getPhysicalAddress());
}

View File

@@ -19,6 +19,7 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.util.Slog;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import java.io.UnsupportedEncodingException;
/**
@@ -164,7 +165,8 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
private void addDeviceInfo() {
// The device should be in the device list with default information.
if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) {
if (!localDevice().mService.getHdmiCecNetwork().isInDeviceList(mDeviceLogicalAddress,
mDevicePhysicalAddress)) {
Slog.w(TAG, String.format("Device not found (%02x, %04x)",
mDeviceLogicalAddress, mDevicePhysicalAddress));
return;
@@ -176,7 +178,7 @@ final class NewDeviceAction extends HdmiCecFeatureAction {
mDeviceLogicalAddress, mDevicePhysicalAddress,
tv().getPortId(mDevicePhysicalAddress),
mDeviceType, mVendorId, mDisplayName);
tv().addCecDevice(deviceInfo);
localDevice().mService.getHdmiCecNetwork().addCecDevice(deviceInfo);
// Consume CEC messages we already got for this newly found device.
tv().processDelayedMessages(mDeviceLogicalAddress);

View File

@@ -20,7 +20,9 @@ import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_UNKNOWN;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.SparseIntArray;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
import java.util.List;
/**
@@ -111,7 +113,8 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
}
private void queryPowerStatus() {
List<HdmiDeviceInfo> deviceInfos = tv().getDeviceInfoList(false);
List<HdmiDeviceInfo> deviceInfos =
localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false);
resetPowerStatus(deviceInfos);
for (HdmiDeviceInfo info : deviceInfos) {
final int logicalAddress = info.getLogicalAddress();
@@ -137,7 +140,8 @@ public class PowerStatusMonitorAction extends HdmiCecFeatureAction {
}
private void updatePowerStatus(int logicalAddress, int newStatus, boolean remove) {
tv().updateDevicePowerStatus(logicalAddress, newStatus);
localDevice().mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
newStatus);
if (remove) {
mPowerStatus.delete(logicalAddress);

View File

@@ -17,8 +17,8 @@
package com.android.server.hdmi;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -160,7 +160,9 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
HdmiDeviceInfo device = tv().getDeviceInfoByPath(mCurrentRoutingPath);
HdmiDeviceInfo device =
localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
mCurrentRoutingPath);
if (device != null && mQueryDevicePowerStatus) {
int deviceLogicalAddress = device.getLogicalAddress();
queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {

View File

@@ -114,7 +114,7 @@ public class ActiveSourceActionTest {
mHdmiControlService.setCecController(hdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiControlService.initPortInfo();
mHdmiControlService.initService();
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();

View File

@@ -123,7 +123,7 @@ public class ArcInitiationActionFromAvrTest {
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
hdmiControlService.initPortInfo();
hdmiControlService.initService();
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);

View File

@@ -117,7 +117,7 @@ public class ArcTerminationActionFromAvrTest {
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
hdmiControlService.initPortInfo();
hdmiControlService.initService();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override

View File

@@ -30,6 +30,7 @@ import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
@@ -55,6 +56,7 @@ final class FakeNativeWrapper implements NativeWrapper {
};
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
private final Map<Integer, Boolean> mPortConnectionStatus = new HashMap<>();
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
@@ -125,7 +127,12 @@ final class FakeNativeWrapper implements NativeWrapper {
@Override
public boolean nativeIsConnected(int port) {
return false;
Boolean isConnected = mPortConnectionStatus.get(port);
return isConnected == null ? false : isConnected;
}
public void setPortConnectionStatus(int port, boolean connected) {
mPortConnectionStatus.put(port, connected);
}
public void onCecMessage(HdmiCecMessage hdmiCecMessage) {

View File

@@ -15,13 +15,9 @@
*/
package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE;
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_TUNER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.MESSAGE_GIVE_AUDIO_STATUS;
@@ -226,7 +222,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
new HdmiPortInfo(
4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initPortInfo();
mHdmiControlService.initService();
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -653,75 +649,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void updateCecDevice_deviceNotExists_addDevice() {
assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
assertThat(mDeviceInfo).isEqualTo(newDevice);
assertThat(mHdmiCecLocalDeviceAudioSystem
.getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
}
@Test
public void updateCecDevice_deviceExists_doNothing() {
mInvokeDeviceEventState = 0;
HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
assertThat(mInvokeDeviceEventState).isEqualTo(0);
}
@Test
public void updateCecDevice_deviceInfoDifferent_updateDevice() {
assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_1, 0x2300, 4, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
mHdmiCecLocalDeviceAudioSystem.updateCecDevice(differentDevice);
assertThat(mDeviceInfo).isEqualTo(differentDevice);
assertThat(mHdmiCecLocalDeviceAudioSystem
.getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
}
@Test
@Ignore("b/120845532")
public void handleReportPhysicalAddress_differentPath_addDevice() {
assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
ADDR_PLAYBACK_2, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_2));
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(
ADDR_PLAYBACK_2, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
mTestLooper.dispatchAll();
assertThat(mDeviceInfo).isEqualTo(differentDevice);
assertThat(mHdmiCecLocalDeviceAudioSystem
.getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
}
@Test
public void doNotWakeUpOnHotPlug_PlugIn() {
mWokenUp = false;

View File

@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -125,7 +126,12 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
mHdmiControlService.initPortInfo();
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
@@ -892,6 +898,7 @@ public class HdmiCecLocalDevicePlaybackTest {
public void handleSetStreamPath_afterHotplug_broadcastsActiveSource() {
mNativeWrapper.onHotplugEvent(1, false);
mNativeWrapper.onHotplugEvent(1, true);
mTestLooper.dispatchAll();
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);

View File

@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Handler;
import android.os.IPowerManager;
@@ -103,7 +104,11 @@ public class HdmiCecLocalDeviceTvTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceTv);
mHdmiControlService.initPortInfo();
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
hdmiPortInfos[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTvPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
@@ -119,8 +124,9 @@ public class HdmiCecLocalDeviceTvTest {
@Test
public void onAddressAllocated_invokesDeviceDiscovery() {
mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecLocalDeviceTv.onAddressAllocated(0, HdmiControlService.INITIATED_BY_BOOT_UP);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();

View File

@@ -0,0 +1,400 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.hdmi;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.List;
/**
* Tests for {@link HdmiCecNetwork} class.
*/
@SmallTest
@Presubmit
@RunWith(JUnit4.class)
public class HdmiCecNetworkTest {
private HdmiCecNetwork mHdmiCecNetwork;
private Context mContext;
private HdmiControlService mHdmiControlService;
private HdmiMhlControllerStub mHdmiMhlControllerStub;
private HdmiCecController mHdmiCecController;
private FakeNativeWrapper mNativeWrapper;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private HdmiPortInfo[] mHdmiPortInfo;
private List<Integer> mDeviceEventListenerStatuses = new ArrayList<>();
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mHdmiControlService = new HdmiControlService(mContext) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListenerStatuses.add(status);
}
};
mMyLooper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(mHdmiControlService,
mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlService);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(mHdmiMhlControllerStub);
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlService,
mHdmiCecController, mHdmiMhlControllerStub);
mHdmiControlService.setHdmiCecNetwork(mHdmiCecNetwork);
mHdmiPortInfo = new HdmiPortInfo[5];
mHdmiPortInfo[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
mHdmiPortInfo[1] =
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
mHdmiPortInfo[2] =
new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
mHdmiPortInfo[3] =
new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mHdmiPortInfo[4] =
new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiCecNetwork.initPortInfo();
}
@Test
public void initializeNetwork_verifyPortInfo() {
mHdmiCecNetwork.initPortInfo();
assertThat(mHdmiCecNetwork.getPortInfo()).hasSize(mHdmiPortInfo.length);
}
@Test
public void physicalAddressToPort_pathExists_weAreNonTv() {
mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiCecNetwork.initPortInfo();
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2120)).isEqualTo(1);
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2234)).isEqualTo(2);
}
@Test
public void physicalAddressToPort_pathExists_weAreSourceDevice() {
mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiCecNetwork.initPortInfo();
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x0000)).isEqualTo(5);
}
@Test
public void physicalAddressToPort_pathExists_weAreTv() {
mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiCecNetwork.initPortInfo();
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2120)).isEqualTo(3);
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x3234)).isEqualTo(4);
}
@Test
public void physicalAddressToPort_pathInvalid() {
mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiCecNetwork.initPortInfo();
assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x1000)).isEqualTo(
Constants.INVALID_PORT_ID);
}
@Test
public void localDevices_verifyOne_tv() {
mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV,
new HdmiCecLocalDeviceTv(mHdmiControlService));
assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
HdmiCecLocalDeviceTv.class);
assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV)).isNotNull();
assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK)).isNull();
}
@Test
public void localDevices_verifyOne_playback() {
mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
new HdmiCecLocalDevicePlayback(mHdmiControlService));
assertThat(mHdmiCecNetwork.getLocalDeviceList()).hasSize(1);
assertThat(mHdmiCecNetwork.getLocalDeviceList().get(0)).isInstanceOf(
HdmiCecLocalDevicePlayback.class);
assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK)).isNotNull();
assertThat(mHdmiCecNetwork.getLocalDevice(HdmiDeviceInfo.DEVICE_TV)).isNull();
}
@Test
public void cecDevices_tracking_logicalAddressOnly() throws Exception {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
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
public void cecDevices_tracking_logicalAddressOnly_doesntNotifyAgain() throws Exception {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
assertThat(mDeviceEventListenerStatuses).containsExactly(
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
@Test
public void cecDevices_tracking_reportPhysicalAddress() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int physicalAddress = 0x1000;
int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
physicalAddress, type));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
physicalAddress);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
}
@Test
public void cecDevices_tracking_updateDeviceInfo_sameDoesntNotify() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int physicalAddress = 0x1000;
int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
physicalAddress, type));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
physicalAddress, type));
// ADD for logical address first detected
// UPDATE for updating device with physical address
assertThat(mDeviceEventListenerStatuses).containsExactly(
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
@Test
public void cecDevices_tracking_reportPowerStatus() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int powerStatus = HdmiControlManager.POWER_STATUS_ON;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
Constants.ADDR_BROADCAST, powerStatus));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
}
@Test
public void cecDevices_tracking_reportOsdName() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
String osdName = "Test Device";
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
Constants.ADDR_BROADCAST, osdName));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
}
@Test
public void cecDevices_tracking_updatesDeviceInfo() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int physicalAddress = 0x1000;
int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
int powerStatus = HdmiControlManager.POWER_STATUS_ON;
String osdName = "Test Device";
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
physicalAddress, type));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
Constants.ADDR_BROADCAST, powerStatus));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
Constants.ADDR_BROADCAST, osdName));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(physicalAddress);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
}
@Test
public void cecDevices_tracking_updatesPhysicalAddress() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int initialPhysicalAddress = 0x1000;
int updatedPhysicalAddress = 0x2000;
int type = HdmiDeviceInfo.DEVICE_PLAYBACK;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
initialPhysicalAddress, type));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(logicalAddress,
updatedPhysicalAddress, type));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(updatedPhysicalAddress);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
// ADD for logical address first detected
// UPDATE for updating device with physical address
// 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);
}
@Test
public void cecDevices_tracking_updatesPowerStatus() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
int powerStatus = HdmiControlManager.POWER_STATUS_ON;
int updatedPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
Constants.ADDR_BROADCAST, powerStatus));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildReportPowerStatus(logicalAddress,
Constants.ADDR_BROADCAST, updatedPowerStatus));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(updatedPowerStatus);
}
@Test
public void cecDevices_tracking_updatesOsdName() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
String osdName = "Test Device";
String updatedOsdName = "Different";
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
Constants.ADDR_BROADCAST, osdName));
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildSetOsdNameCommand(logicalAddress,
Constants.ADDR_BROADCAST, updatedOsdName));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
HdmiDeviceInfo cecDeviceInfo = mHdmiCecNetwork.getCecDeviceInfo(logicalAddress);
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(updatedOsdName);
}
@Test
public void cecDevices_tracking_clearDevices() {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
mHdmiCecNetwork.handleCecMessage(
HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
mHdmiCecNetwork.clearDeviceList();
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).isEmpty();
assertThat(mDeviceEventListenerStatuses).containsExactly(
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
}

View File

@@ -21,11 +21,13 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Looper;
import android.os.test.TestLooper;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -44,6 +46,8 @@ import java.util.ArrayList;
@RunWith(JUnit4.class)
public class HdmiControlServiceBinderAPITest {
private Context mContext;
private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
private boolean mCanGoToStandby;
@@ -111,8 +115,12 @@ public class HdmiControlServiceBinderAPITest {
@Before
public void SetUp() {
mContext = InstrumentationRegistry.getTargetContext();
// Some tests expect no logical addresses being allocated at the beginning of the test.
setHdmiControlEnabled(false);
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
new HdmiControlService(mContext) {
@Override
void sendCecCommand(HdmiCecMessage command) {
switch (command.getOpcode()) {
@@ -164,7 +172,7 @@ public class HdmiControlServiceBinderAPITest {
mHdmiPortInfo[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initPortInfo();
mHdmiControlService.initService();
mResult = -1;
mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
@@ -183,6 +191,7 @@ public class HdmiControlServiceBinderAPITest {
assertEquals(mResult, -1);
assertThat(mPlaybackDevice.isActiveSource()).isFalse();
setHdmiControlEnabled(true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
@@ -192,6 +201,8 @@ public class HdmiControlServiceBinderAPITest {
@Test
public void oneTouchPlay_addressAllocated() {
setHdmiControlEnabled(true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
@@ -204,4 +215,10 @@ public class HdmiControlServiceBinderAPITest {
assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
assertThat(mPlaybackDevice.isActiveSource()).isTrue();
}
private void setHdmiControlEnabled(boolean enabled) {
int value = enabled ? 1 : 0;
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.HDMI_CONTROL_ENABLED,
value);
}
}

View File

@@ -176,7 +176,7 @@ public class HdmiControlServiceTest {
mHdmiPortInfo[3] =
new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initPortInfo();
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -205,29 +205,6 @@ public class HdmiControlServiceTest {
assertTrue(mMyAudioSystemDevice.isDisabled());
}
@Test
public void pathToPort_pathExists_weAreNonTv() {
mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiControlService.initPortInfo();
assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
}
@Test
public void pathToPort_pathExists_weAreTv() {
mNativeWrapper.setPhysicalAddress(0x0000);
mHdmiControlService.initPortInfo();
assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
}
@Test
public void pathToPort_pathInvalid() {
mNativeWrapper.setPhysicalAddress(0x2000);
mHdmiControlService.initPortInfo();
assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
}
@Test
public void initialPowerStatus_normalBoot_isTransientToStandby() {
assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(

View File

@@ -90,8 +90,6 @@ public class SystemAudioInitiationActionFromAvrTest {
break;
case Constants.MESSAGE_INITIATE_ARC:
break;
default:
throw new IllegalArgumentException("Unexpected message");
}
}
@@ -159,6 +157,14 @@ public class SystemAudioInitiationActionFromAvrTest {
return -1;
}
};
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
HdmiCecController.NativeWrapper nativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
hdmiControlService, nativeWrapper, hdmiControlService.getAtomWriter());
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.initService();
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -181,8 +187,6 @@ public class SystemAudioInitiationActionFromAvrTest {
}
};
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
}
@Test