Merge "Track HDMI CEC Network"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d2aa0281d
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
822
services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
Normal file
822
services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user