summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java31
-rw-r--r--core/java/android/hardware/hdmi/HdmiSwitchClient.java137
-rw-r--r--core/java/com/android/internal/os/RoSystemProperties.java9
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java8
-rwxr-xr-xservices/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java17
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java5
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java279
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java25
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java36
13 files changed, 646 insertions, 48 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index b520d2c14edc..f5d288e6a233 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -16,6 +16,8 @@
package android.hardware.hdmi;
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -27,6 +29,7 @@ import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
@@ -264,6 +267,10 @@ public final class HdmiControlManager {
private final boolean mHasTvDevice;
// True if we have a logical device of type audio system hosted in the system.
private final boolean mHasAudioSystemDevice;
+ // True if we have a logical device of type audio system hosted in the system.
+ private final boolean mHasSwitchDevice;
+ // True if it's a switch device.
+ private final boolean mIsSwitchDevice;
/**
* {@hide} - hide this constructor because it has a parameter of type IHdmiControlService,
@@ -283,6 +290,9 @@ public final class HdmiControlManager {
mHasTvDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_TV);
mHasPlaybackDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PLAYBACK);
mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+ mIsSwitchDevice = SystemProperties.getBoolean(
+ PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
}
private static boolean hasDeviceType(int[] types, int type) {
@@ -319,6 +329,9 @@ public final class HdmiControlManager {
return mHasPlaybackDevice ? new HdmiPlaybackClient(mService) : null;
case HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM:
return mHasAudioSystemDevice ? new HdmiAudioSystemClient(mService) : null;
+ case HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH:
+ return (mHasSwitchDevice || mIsSwitchDevice)
+ ? new HdmiSwitchClient(mService) : null;
default:
return null;
}
@@ -373,6 +386,24 @@ public final class HdmiControlManager {
}
/**
+ * Gets an object that represents an HDMI-CEC logical device of type switch on the system.
+ *
+ * <p>Used to send HDMI control messages to other devices like TV through HDMI bus. It is also
+ * possible to communicate with other logical devices hosted in the same system if the system is
+ * configured to host more than one type of HDMI-CEC logical devices.
+ *
+ * @return {@link HdmiSwitchClient} instance. {@code null} on failure.
+ *
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ @Nullable
+ @SuppressLint("Doclava125")
+ public HdmiSwitchClient getSwitchClient() {
+ return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
+ }
+
+ /**
* Controls standby mode of the system. It will also try to turn on/off the connected devices if
* necessary.
*
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
new file mode 100644
index 000000000000..1ac29736f964
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 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 android.hardware.hdmi;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * HdmiSwitchClient represents HDMI-CEC logical device of type Switch in the Android system which
+ * acts as switch.
+ *
+ * <p>HdmiSwitchClient has a CEC device type of HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH,
+ * but it is used by all Android TV devices that have multiple HDMI inputs,
+ * even if it is not a "pure" swicth and has another device type like TV or Player.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+public class HdmiSwitchClient extends HdmiClient {
+
+ private static final String TAG = "HdmiSwitchClient";
+
+ /* package */ HdmiSwitchClient(IHdmiControlService service) {
+ super(service);
+ }
+
+ private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+ return new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ callback.onComplete(result);
+ }
+ };
+ }
+
+ /** @hide */
+ // TODO(b/110094868): unhide for Q
+ @Override
+ public int getDeviceType() {
+ return HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH;
+ }
+
+ /**
+ * Selects a CEC logical device to be a new active source.
+ *
+ * @param logicalAddress logical address of the device to select
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+ public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null.");
+ }
+ try {
+ mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select device: ", e);
+ }
+ }
+
+ /**
+ * Selects a HDMI port to be a new route path.
+ *
+ * @param portId HDMI port to select
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+ public void portSelect(int portId, @NonNull SelectCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ try {
+ mService.portSelect(portId, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select port: ", e);
+ }
+ }
+
+ /**
+ * Returns all the CEC devices connected to the device.
+ *
+ * <p>This only applies to device with multiple HDMI inputs
+ *
+ * @return list of {@link HdmiDeviceInfo} for connected CEC devices. Empty list is returned if
+ * there is none.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+ public List<HdmiDeviceInfo> getDeviceList() {
+ try {
+ return mService.getDeviceList();
+ } catch (RemoteException e) {
+ Log.e("TAG", "Failed to call getDeviceList():", e);
+ return Collections.<HdmiDeviceInfo>emptyList();
+ }
+ }
+
+ /**
+ * Callback interface used to get the result of {@link #deviceSelect} or {@link #portSelect}.
+ *
+ * @hide
+ * TODO(b/110094868): unhide and add @SystemApi for Q
+ */
+ public interface SelectCallback {
+
+ /**
+ * Called when the operation is finished.
+ *
+ * @param result the result value of {@link #deviceSelect} or {@link #portSelect}.
+ */
+ void onComplete(int result);
+ }
+}
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index a319d83b077d..b529bbe51e93 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -30,6 +30,7 @@ public class RoSystemProperties {
public static final String CONTROL_PRIVAPP_PERMISSIONS =
SystemProperties.get("ro.control_privapp_permissions");
+ // ------ ro.hdmi.* -------- //
/**
* Property to indicate if a CEC audio device should forward volume keys when system audio
* mode is off.
@@ -38,6 +39,14 @@ public class RoSystemProperties {
SystemProperties.getBoolean(
"ro.hdmi.cec_audio_device_forward_volume_keys_system_audio_mode_off", false);
+ /**
+ * Property to indicate if the current device is a cec switch device.
+ *
+ * <p> Default is false.
+ */
+ public static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
+ "ro.hdmi.property_is_device_hdmi_cec_switch";
+
// ------ ro.config.* -------- //
public static final boolean CONFIG_AVOID_GFX_ACCEL =
SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 6f5a19612895..ff029c194f12 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -343,14 +343,6 @@ final class Constants {
static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
"ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
- /**
- * Property to indicate if the current device is a cec switch device.
- *
- * <p> Default is false.
- */
- static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH =
- "ro.hdmi.property_is_device_hdmi_cec_switch";
-
// Set to false to allow playback device to go to suspend mode even
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index b75e75feabb3..ba21b7882afd 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -28,7 +28,7 @@ import java.util.List;
/**
* Feature action that handles device discovery sequences.
- * Device discovery is launched when TV device is woken from "Standby" state
+ * Device discovery is launched when device is woken from "Standby" state
* or enabled "Control for Hdmi" from disabled state.
*
* <p>Device discovery goes through the following steps.
@@ -89,6 +89,7 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
private final DeviceDiscoveryCallback mCallback;
private int mProcessedDeviceCount = 0;
private int mTimeoutRetry = 0;
+ private boolean mIsTvDevice = source().mService.isTvDevice();
/**
* Constructor.
@@ -266,15 +267,19 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
current.mPortId = getPortId(current.mPhysicalAddress);
current.mDeviceType = params[2] & 0xFF;
- tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
+ // TODO(amyjojo): check if non-TV device needs to update cec switch info.
+ // This is to manager CEC device separately in case they don't have address.
+ if (mIsTvDevice) {
+ tv().updateCecSwitchInfo(current.mLogicalAddress, current.mDeviceType,
current.mPhysicalAddress);
-
+ }
increaseProcessedDeviceCount();
checkAndProceedStage();
}
private int getPortId(int physicalAddress) {
- return tv().getPortId(physicalAddress);
+ return mIsTvDevice ? tv().getPortId(physicalAddress)
+ : source().getPortId(physicalAddress);
}
private void handleSetOsdName(HdmiCecMessage cmd) {
@@ -345,7 +350,9 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
mCallback.onDeviceDiscoveryDone(result);
finish();
// Process any commands buffered while device discovery action was in progress.
- tv().processAllDelayedMessages();
+ if (mIsTvDevice) {
+ tv().processAllDelayedMessages();
+ }
}
private void checkAndProceedStage() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 6e1b0181adbf..32dc02613512 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -916,6 +916,11 @@ abstract class HdmiCecLocalDevice {
setActivePath(mService.portIdToPath(portId));
}
+ // Returns the id of the port that the target device is connected to.
+ int getPortId(int physicalAddress) {
+ return mService.pathToPortId(physicalAddress);
+ }
+
@ServiceThreadOnly
HdmiCecMessageCache getCecMessageCache() {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 8cfe47f3d75e..0e4e3342451c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -23,19 +23,25 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.tv.TvContract;
import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.Constants.AudioCodec;
+import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+import java.io.UnsupportedEncodingException;
import java.util.HashMap;
+import java.util.List;
/**
* Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
@@ -71,6 +77,10 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// processing.
private final HashMap<Integer, String> mTvInputs = new HashMap<>();
+ // Map-like container of all cec devices.
+ // device id is used as key of container.
+ private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
+
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
mSystemAudioControlFeatureEnabled = true;
@@ -86,6 +96,132 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
"com.droidlogic.tvinput/.services.Hdmi3InputService/HW7");
}
+ /**
+ * 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();
+ HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
+ if (oldDeviceInfo != null) {
+ removeDeviceInfo(deviceInfo.getId());
+ }
+ mDeviceInfos.append(deviceInfo.getId(), deviceInfo);
+ 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);
+ }
+ 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));
+ }
+
+ private void invokeDeviceEventListener(HdmiDeviceInfo info, int status) {
+ mService.invokeDeviceEventListeners(info, status);
+ }
+
+ @Override
+ @ServiceThreadOnly
+ void onHotplug(int portId, boolean connected) {
+ assertRunOnServiceThread();
+ if (connected) {
+ mService.wakeUp();
+ }
+ if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+ mCecMessageCache.flushAll();
+ } else {
+ if (connected) {
+ launchDeviceDiscovery();
+ } else {
+ // TODO(amyjojo): remove device from mDeviceInfo
+ }
+ }
+ }
+
@Override
@ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
@@ -116,6 +252,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
boolean lastSystemAudioControlStatus =
SystemProperties.getBoolean(Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL, true);
systemAudioControlOnPowerOn(systemAudioControlOnPowerOnProp, lastSystemAudioControlStatus);
+ clearDeviceInfoList();
+ launchDeviceDiscovery();
startQueuedActions();
}
@@ -152,6 +290,78 @@ 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, HdmiUtils.getDefaultDeviceName(address)));
+ // 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().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 handleReportAudioStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
// TODO(amyjojo): implement report audio status handler
@@ -407,8 +617,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug(
"System Audio Mode change[old:%b new:%b]",
mSystemAudioActivated, newSystemAudioMode);
- // Wake up device if System Audio Control is turned on but device is still on standby
- if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
+ // Wake up device if System Audio Control is turned on
+ if (newSystemAudioMode) {
mService.wakeUp();
}
setSystemAudioMode(newSystemAudioMode);
@@ -494,19 +704,20 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
}
- getActiveSource().invalidate();
if (!mService.isControlEnabled()) {
+ setRoutingPort(portId);
setLocalActivePort(portId);
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
- int oldPath = getLocalActivePort() != Constants.CEC_SWITCH_HOME
- ? getActivePathOnSwitchFromActivePortId(getLocalActivePort())
+ int oldPath = getRoutingPort() != Constants.CEC_SWITCH_HOME
+ ? mService.portIdToPath(getRoutingPort())
: getDeviceInfo().getPhysicalAddress();
- int newPath = getActivePathOnSwitchFromActivePortId(portId);
+ int newPath = mService.portIdToPath(portId);
if (oldPath == newPath) {
return;
}
+ setRoutingPort(portId);
setLocalActivePort(portId);
HdmiCecMessage routingChange =
HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
@@ -575,10 +786,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
return;
}
- // Wake up device if it is still on standby
- if (mService.isPowerStandbyOrTransient()) {
- mService.wakeUp();
- }
+ // Wake up device
+ mService.wakeUp();
// Check if TV supports System Audio Control.
// Handle broadcasting setSystemAudioMode on or aborting message on callback.
queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
@@ -635,9 +844,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return;
}
// Wake up if the current device if ready to route.
- if (mService.isPowerStandbyOrTransient()) {
- mService.wakeUp();
- }
+ mService.wakeUp();
if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
switchToHomeTvInput();
} else if (portId == Constants.CEC_SWITCH_ARC) {
@@ -725,4 +932,50 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mAddress, routingInformationPath));
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();
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
+ new DeviceDiscoveryCallback() {
+ @Override
+ public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
+ for (HdmiDeviceInfo info : deviceInfos) {
+ 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();
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 6532e16cffe5..a95f7f1f3f3d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -16,7 +16,10 @@
package com.android.server.hdmi;
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.SystemProperties;
import android.util.Slog;
@@ -42,7 +45,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
// Device has cec switch functionality or not.
// Default is false.
protected boolean mIsSwitchDevice = SystemProperties.getBoolean(
- Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+ PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
// Routing port number used for Routing Control.
// This records the default routing port or the previous valid routing port.
@@ -71,9 +74,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
- mCecMessageCache.flushAll();
+ if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+ mCecMessageCache.flushAll();
+ }
// We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
- if (mService.isPowerStandbyOrTransient()) {
+ if (connected) {
mService.wakeUp();
}
}
@@ -117,6 +122,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
setActiveSource(activeSource);
}
setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
+ updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
switchInputOnReceivingNewActivePath(physicalAddress);
return true;
}
@@ -185,6 +191,13 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
// do nothing
}
+ // Update the power status of the devices connected to the current device.
+ // This only works if the current device is a switch and keeps tracking the device info
+ // of the device connected to it.
+ protected void updateDevicePowerStatus(int logicalAddress, int newPowerStatus) {
+ // do nothing
+ }
+
// Active source claiming needs to be handled in Service
// since service can decide who will be the active source when the device supports
// multiple device types in this method.
@@ -204,10 +217,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
if (!mIsActiveSource) {
return;
}
- // Wake up the device if the power is in standby mode
- if (mService.isPowerStandbyOrTransient()) {
- mService.wakeUp();
- }
+ // Wake up the device
+ mService.wakeUp();
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index b91d8c637c79..a8c435086e8e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -346,10 +346,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
}
- int getPortId(int physicalAddress) {
- return mService.pathToPortId(physicalAddress);
- }
-
/**
* Returns the previous port id kept to handle input switching on <Inactive Source>.
*/
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 833091df5f1c..2d6e76294e4a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
+import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
import static com.android.server.hdmi.Constants.DISABLED;
import static com.android.server.hdmi.Constants.ENABLED;
import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE;
@@ -754,6 +755,9 @@ public class HdmiControlService extends SystemService {
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) {
@@ -808,13 +812,31 @@ public class HdmiControlService extends SystemService {
}
/**
- * Returns the id of HDMI port located at the top of the hierarchy of
- * the specified routing path. For the routing path 0x1220 (1.2.2.0), for instance,
- * the port id to be returned is the ID associated with the port address
- * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
+ * 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 pathToPortId(int path) {
- int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
+ int mask = 0xF000;
+ int finalMask = 0xF000;
+ int 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);
}
@@ -1007,8 +1029,9 @@ public class HdmiControlService extends SystemService {
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
- if (connected && !isTvDevice()) {
- if (getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT && isSwitchDevice()) {
+ if (connected && !isTvDevice()
+ && getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
+ if (isSwitchDevice()) {
initPortInfo();
HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
}
@@ -2130,7 +2153,7 @@ public class HdmiControlService extends SystemService {
boolean isSwitchDevice() {
return SystemProperties.getBoolean(
- Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+ PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
}
boolean isTvDeviceEnabled() {
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 4255e37aac2d..8607ec66a5ba 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -50,6 +50,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
private int mMyPhysicalAddress = 0;
+ private HdmiPortInfo[] mHdmiPortInfo = null;
@Override
public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -92,9 +93,11 @@ final class FakeNativeWrapper implements NativeWrapper {
@Override
public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
- HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[1];
- hdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
- return hdmiPortInfo;
+ if (mHdmiPortInfo == null) {
+ mHdmiPortInfo = new HdmiPortInfo[1];
+ mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
+ }
+ return mHdmiPortInfo;
}
@Override
@@ -131,4 +134,10 @@ final class FakeNativeWrapper implements NativeWrapper {
protected void setPhysicalAddress(int physicalAddress) {
mMyPhysicalAddress = physicalAddress;
}
+
+ @VisibleForTesting
+ protected void setPortInfo(HdmiPortInfo[] hdmiPortInfo) {
+ mHdmiPortInfo = new HdmiPortInfo[hdmiPortInfo.length];
+ System.arraycopy(hdmiPortInfo, 0, mHdmiPortInfo, 0, hdmiPortInfo.length);
+ }
}
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 b47f269bc721..3b51a2a70f83 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -15,6 +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;
@@ -25,6 +28,7 @@ import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
import static com.google.common.truth.Truth.assertThat;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioManager;
import android.os.Looper;
import android.os.SystemProperties;
@@ -64,6 +68,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
private int mMusicMaxVolume;
private boolean mMusicMute;
private int mAvrPhysicalAddress;
+ private int mInvokeDeviceEventState;
+ private HdmiDeviceInfo mDeviceInfo;
@Before
public void setUp() {
@@ -127,6 +133,18 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Override
void wakeUp() {}
+
+ @Override
+ void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+ mDeviceInfo = device;
+ mInvokeDeviceEventState = status;
+ }
+
+ @Override
+ int pathToPortId(int path) {
+ // port id is not useful for the test right now
+ return 1;
+ }
};
mMyLooper = mTestLooper.getLooper();
@@ -157,6 +175,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress);
SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true");
+ mInvokeDeviceEventState = 0;
+ mDeviceInfo = null;
}
@Test
@@ -611,4 +631,73 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
+
+ @Test
+ public void updateCecDevice_deviceNotExists_addDevice() {
+ assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
+ HdmiDeviceInfo newDevice = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+
+ mHdmiCecLocalDeviceAudioSystem.updateCecDevice(newDevice);
+ assertThat(mDeviceInfo).isEqualTo(newDevice);
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getCecDeviceInfo(newDevice.getLogicalAddress())).isEqualTo(newDevice);
+ assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+ }
+
+ @Test
+ public void updateCecDevice_deviceExists_doNothing() {
+ mInvokeDeviceEventState = 0;
+ HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+ mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+ mHdmiCecLocalDeviceAudioSystem.updateCecDevice(oldDevice);
+ assertThat(mInvokeDeviceEventState).isEqualTo(0);
+ }
+
+ @Test
+ public void updateCecDevice_deviceInfoDifferent_updateDevice() {
+ assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_UPDATE_DEVICE);
+ HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_1, 0x2100, 2, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+ mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+
+ HdmiDeviceInfo differentDevice = new HdmiDeviceInfo(
+ ADDR_PLAYBACK_1, 0x2100, 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
+ 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_1, 0x2200, 1, HdmiDeviceInfo.DEVICE_PLAYBACK,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(ADDR_PLAYBACK_1));
+ HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
+ .buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_1, 0x2200, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mHdmiCecLocalDeviceAudioSystem.handleReportPhysicalAddress(reportPhysicalAddress);
+
+ mHdmiCecLocalDeviceAudioSystem.addDeviceInfo(oldDevice);
+ mTestLooper.dispatchAll();
+ assertThat(mDeviceInfo).isEqualTo(differentDevice);
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getCecDeviceInfo(differentDevice.getLogicalAddress())).isEqualTo(differentDevice);
+ assertThat(mInvokeDeviceEventState).isEqualTo(DEVICE_EVENT_ADD_DEVICE);
+ }
}
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 18c9a653dc7d..67ce13fdef72 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -20,13 +20,18 @@ import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.hardware.hdmi.HdmiPortInfo;
import android.os.Looper;
import android.os.test.TestLooper;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -102,6 +107,7 @@ public class HdmiControlServiceTest {
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private boolean mStandbyMessageReceived;
+ private HdmiPortInfo[] mHdmiPortInfo;
@Before
public void SetUp() {
@@ -131,6 +137,16 @@ public class HdmiControlServiceTest {
mLocalDevices.add(mMyAudioSystemDevice);
mLocalDevices.add(mMyPlaybackDevice);
+ mHdmiPortInfo = new HdmiPortInfo[4];
+ 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);
+ mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initPortInfo();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -159,4 +175,24 @@ public class HdmiControlServiceTest {
assertTrue(mMyPlaybackDevice.isDisabled());
assertTrue(mMyAudioSystemDevice.isDisabled());
}
+
+ @Test
+ public void pathToPort_pathExists_weAreNonTv() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(1);
+ assertThat(mHdmiControlService.pathToPortId(0x2234)).isEqualTo(2);
+ }
+
+ @Test
+ public void pathToPort_pathExists_weAreTv() {
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ assertThat(mHdmiControlService.pathToPortId(0x2120)).isEqualTo(3);
+ assertThat(mHdmiControlService.pathToPortId(0x3234)).isEqualTo(4);
+ }
+
+ @Test
+ public void pathToPort_pathInvalid() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
+ }
}