summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2020-10-26 13:43:07 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-10-26 13:43:07 +0000
commit9d2aa0281d9bddca0d941978e26b59ba363909ff (patch)
tree534fa04d6fc387300ca573217397f0f9ef74dba9
parent90d0b580026ac40fe75eb0da0784c6603321180b (diff)
parent59841e039772887e2e31eede27141522127f8607 (diff)
Merge "Track HDMI CEC Network"
-rwxr-xr-xservices/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java75
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java27
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java266
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java466
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java822
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java360
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java7
-rw-r--r--services/core/java/com/android/server/hdmi/NewDeviceAction.java6
-rw-r--r--services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java8
-rw-r--r--services/core/java/com/android/server/hdmi/RoutingControlAction.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java400
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java12
22 files changed, 1482 insertions, 1131 deletions
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index a9e8719890ef..8980de12a31f 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -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();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 96679c3ab767..efe730231d36 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -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}.
@@ -269,17 +259,6 @@ final class HdmiCecController {
}
/**
- * 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
* a command having available destinations.
@@ -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;
@@ -475,17 +434,6 @@ final class HdmiCecController {
}
@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,
final DevicePollingCallback callback, final List<Integer> allocated) {
@@ -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);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 946fb0d00d60..62a67b6243d7 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -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) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 29bdd6cb40c3..fe4fd3805994 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -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);
@@ -474,79 +333,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@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) {
assertRunOnServiceThread();
// TODO(amyjojo): implement initiate arc handler
@@ -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);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 0325ad929849..93cdca2d0c83 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -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();
@@ -220,17 +194,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();
mSelectRequestBuffer = requestBuffer;
@@ -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,42 +513,20 @@ 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.
return true;
@@ -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);
- }
-
- /**
- * 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);
- }
+ return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
}
- /**
- * 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();
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
new file mode 100644
index 000000000000..416302419a71
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -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();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8bb89da5726f..ee86593916ae 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -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,6 +487,26 @@ public class HdmiControlService extends SystemService {
@Override
public void onStart() {
+ initService();
+ publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
+
+ if (mCecController != null) {
+ // Register broadcast receiver for power state change.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SHUTDOWN);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
+
+ // Register ContentObserver to monitor the settings change.
+ registerContentObserver();
+ }
+ mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
+ }
+
+ @VisibleForTesting
+ void initService() {
if (mIoLooper == null) {
mIoThread.start();
mIoLooper = mIoThread.getLooper();
@@ -521,13 +521,7 @@ public class HdmiControlService extends SystemService {
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 {
+ if (mCecController == null) {
Slog.i(TAG, "Device does not support HDMI-CEC.");
return;
}
@@ -537,27 +531,18 @@ public class HdmiControlService extends SystemService {
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();
- initPortInfo();
+ mHdmiCecNetwork.initPortInfo();
if (mMessageValidator == null) {
mMessageValidator = new HdmiCecMessageValidator(this);
}
- publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
-
- if (mCecController != null) {
- // Register broadcast receiver for power state change.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SHUTDOWN);
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter);
-
- // Register ContentObserver to monitor the settings change.
- registerContentObserver();
- }
- mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
}
private void bootCompleted() {
@@ -588,6 +573,15 @@ public class HdmiControlService extends SystemService {
}
@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);
- }
- 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;
+ // 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();
+ }
}
- 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;
- }
- }
-
- void setPortInfo(List<HdmiPortInfo> portInfo) {
- synchronized (mLock) {
- mPortInfo = portInfo;
+ return mHdmiCecNetwork.getPortInfo();
}
}
- /**
- * 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);
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 7670dccf9c0a..ece78bfa2769 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -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());
}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 6753368911b9..edc7bd95a017 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -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);
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index a62d0b63221c..909fcda26c39 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -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);
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 6c8694ea74ad..6c147ed5e6d6 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -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() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 2a9c3942211c..8af7332e24ed 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -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();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 1385376b740d..37a75e3822aa 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -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);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 169f885a7253..6027c3e4eeab 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -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
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 2c42791fabce..bb57a69d6f51 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -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) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 74fd6830de61..c436cc846564 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -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();
@@ -654,75 +650,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@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;
mHdmiCecLocalDeviceAudioSystem.onHotplug(0, true);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index ef98b98ba7e1..1c04d98327c9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -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);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index ce1cdf369076..bf4851b927b1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -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();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
new file mode 100644
index 000000000000..4f18582bf83f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -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);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
index c4068d34c00d..74a00521d7f4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -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);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 2f48b5ee4c70..8f631a307f15 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -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();
@@ -206,29 +206,6 @@ public class HdmiControlServiceTest {
}
@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(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 6be28d9a13be..0e4bfab0d94c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -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