From 60cffce420db4c3395f86d3b9bb36003adf26f5d Mon Sep 17 00:00:00 2001 From: Jungshik Jang Date: Thu, 12 Jun 2014 18:03:04 +0900 Subject: Refine new device action. There are many ways to initiate new device action 1. When receives 2. When receives from unregistered device. If new device is audio system, it should start ARC and system audio initiation action. Along with this consolidate device remove actions. Change-Id: I189afd8bec7270d6a1734a28632593b71932d9e8 --- .../android/server/hdmi/ActiveSourceHandler.java | 5 +- .../android/server/hdmi/DeviceDiscoveryAction.java | 5 +- .../android/server/hdmi/DeviceSelectAction.java | 4 + .../android/server/hdmi/HdmiCecLocalDevice.java | 8 +- .../android/server/hdmi/HdmiCecLocalDeviceTv.java | 54 ++++-- .../com/android/server/hdmi/HdmiConstants.java | 3 + .../android/server/hdmi/HdmiControlService.java | 197 ++++++++++++++++----- .../server/hdmi/HotplugDetectionAction.java | 67 ++++++- .../com/android/server/hdmi/NewDeviceAction.java | 38 +++- .../android/server/hdmi/RoutingControlAction.java | 18 +- 10 files changed, 313 insertions(+), 86 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java index 32bcb691feb4..bcd08ebfb94a 100644 --- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -67,7 +67,10 @@ final class ActiveSourceHandler { } HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress); if (device == null) { - // TODO: Start new device action (Device Discovery) sequence 5. + // "New device action" initiated by does not require + // "Routing change action". + mService.addAndStartAction(new NewDeviceAction(mService, mSourceAddress, + deviceLogicalAddress, routingPath, false)); } if (!mService.isInPresetInstallationMode()) { diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index d36fc2c1f06e..f7392e92b63a 100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -55,9 +55,6 @@ final class DeviceDiscoveryAction extends FeatureAction { private static final int DEVICE_POLLING_RETRY = 1; - // TODO: Move this to common place - private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; - /** * Interface used to report result of device discovery. */ @@ -75,7 +72,7 @@ final class DeviceDiscoveryAction extends FeatureAction { private static final class DeviceInfo { private final int mLogicalAddress; - private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; + private int mPhysicalAddress = HdmiConstants.INVALID_PHYSICAL_ADDRESS; private int mVendorId = HdmiCec.UNKNOWN_VENDOR_ID; private String mDisplayName = ""; private int mDeviceType = HdmiCec.DEVICE_INACTIVE; diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index f170de04b7f4..a8696a156d7d 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -220,4 +220,8 @@ final class DeviceSelectAction extends FeatureAction { Slog.e(TAG, "Callback failed:" + e); } } + + int getTargetAddress() { + return mTarget.getLogicalAddress(); + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 7a08f99f70a1..7a2a6cc2b7bc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -76,7 +76,7 @@ abstract class HdmiCecLocalDevice { return onMessage(message); } - protected boolean onMessage(HdmiCecMessage message) { + protected final boolean onMessage(HdmiCecMessage message) { switch (message.getOpcode()) { case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: return handleGetMenuLanguage(message); @@ -88,6 +88,8 @@ abstract class HdmiCecLocalDevice { return handleGiveDeviceVendorId(); case HdmiCec.MESSAGE_GET_CEC_VERSION: return handleGetCecVersion(message); + case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: + return handleReportPhysicalAddress(message); default: return false; } @@ -143,6 +145,10 @@ abstract class HdmiCecLocalDevice { return false; } + protected boolean handleReportPhysicalAddress(HdmiCecMessage message) { + return false; + } + final void handleAddressAllocated(int logicalAddress) { mAddress = mPreferredAddress = logicalAddress; onAddressAllocated(logicalAddress); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index aa1769e4e389..625b2560d48a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -16,13 +16,15 @@ package com.android.server.hdmi; -import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiControlCallback; import android.os.RemoteException; import android.util.Slog; +import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; + import java.util.Collections; import java.util.List; import java.util.Locale; @@ -46,20 +48,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mAddress, mService.getVendorId())); - mService.launchDeviceDiscovery(mAddress); + launchDeviceDiscovery(); // TODO: Start routing control action, device discovery action. } - @Override - protected boolean onMessage(HdmiCecMessage message) { - switch (message.getOpcode()) { - case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: - return handleReportPhysicalAddress(message); - default: - return super.onMessage(message); - } - } - /** * Performs the action 'device select', or 'one touch play' initiated by TV. * @@ -98,7 +90,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return true; } - private boolean handleReportPhysicalAddress(HdmiCecMessage message) { + @Override + protected boolean handleReportPhysicalAddress(HdmiCecMessage message) { // Ignore if [Device Discovery Action] is going on. if (mService.hasAction(DeviceDiscoveryAction.class)) { Slog.i(TAG, "Ignore unrecognizable " @@ -107,9 +100,15 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); - mService.addAndStartAction(new NewDeviceAction(mService, - mAddress, message.getSource(), physicalAddress)); + int logicalAddress = message.getSource(); + // If it is a new device and connected to the tail of active path, + // it's required to change routing path. + boolean requireRoutingChange = !mService.isInDeviceList(physicalAddress, logicalAddress) + && mService.isTailOfActivePath(physicalAddress); + mService.addAndStartAction(new NewDeviceAction(mService, + mAddress, message.getSource(), physicalAddress, + requireRoutingChange)); return true; } @@ -137,4 +136,29 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiConstants.ABORT_REFUSED)); return true; } + + private void launchDeviceDiscovery() { + mService.clearAllDeviceInfo(); + // TODO: Move the following callback to HdmiLocalDeviceTv. + DeviceDiscoveryAction action = new DeviceDiscoveryAction(mService, mAddress, + new DeviceDiscoveryCallback() { + @Override + public void onDeviceDiscoveryDone(List deviceInfos) { + for (HdmiCecDeviceInfo info : deviceInfos) { + mService.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()) { + mService.addCecDevice(device.getDeviceInfo()); + } + + mService.addAndStartAction(new HotplugDetectionAction(mService, + mAddress)); + } + }); + mService.addAndStartAction(action); + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java index 8f319ea9c02c..2ab87d602a2e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiConstants.java +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -75,5 +75,8 @@ final class HdmiConstants { */ static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3; + static final int INVALID_PORT_ID = -1; + static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF; + private HdmiConstants() { /* cannot be instantiated */ } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index ea375f6c2d47..bccfa364cfb3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -21,6 +21,7 @@ import android.content.Context; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.HdmiHotplugEvent; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlService; @@ -37,7 +38,6 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; -import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import java.util.ArrayList; @@ -296,7 +296,7 @@ public final class HdmiControlService extends SystemService { HdmiPortInfo portInfo = getPortInfo(portId); if (portInfo == null) { Slog.e(TAG, "Cannot find the port info: " + portId); - return 0xFFFF; // Use HdmiConstants.INVALID_PHYSICAL_ADDRESS; + return HdmiConstants.INVALID_PHYSICAL_ADDRESS; } return portInfo.getAddress(); } @@ -314,7 +314,7 @@ public final class HdmiControlService extends SystemService { return info.getId(); } } - return -1; // Use HdmiConstants.INVALID_PORT_ID; + return HdmiConstants.INVALID_PORT_ID; } /** @@ -446,7 +446,12 @@ public final class HdmiControlService extends SystemService { void setSystemAudioMode(boolean on) { synchronized (mLock) { - mSystemAudioMode = on; + if (on != mSystemAudioMode) { + mSystemAudioMode = on; + // TODO: Need to set the preference for SystemAudioMode. + // TODO: Need to handle the notification of changing the mode and + // to identify the notification should be handled in the service or TvSettings. + } } } @@ -456,6 +461,19 @@ public final class HdmiControlService extends SystemService { } } + /** + * Whether a device of the specified physical address is connected to ARC enabled port. + */ + boolean isConnectedToArcPort(int physicalAddress) { + for (HdmiPortInfo portInfo : mPortInfo) { + if (hasSameTopPort(portInfo.getAddress(), physicalAddress) + && portInfo.isArcSupported()) { + return true; + } + } + return false; + } + // See if we have an action of a given type in progress. boolean hasAction(final Class clazz) { for (FeatureAction action : mActions) { @@ -466,6 +484,17 @@ public final class HdmiControlService extends SystemService { return false; } + // Returns all actions matched with given class type. + List getActions(final Class clazz) { + ArrayList actions = new ArrayList<>(); + for (FeatureAction action : mActions) { + if (action.getClass().equals(clazz)) { + actions.add((T) action); + } + } + return actions; + } + /** * Remove the given {@link FeatureAction} object from the action queue. * @@ -529,6 +558,15 @@ public final class HdmiControlService extends SystemService { } } + /** + * Returns whether ARC is enabled or not. + */ + boolean getArcStatus() { + synchronized (mLock) { + return mArcStatusEnabled; + } + } + /** * Transmit a CEC command to CEC bus. * @@ -573,12 +611,23 @@ public final class HdmiControlService extends SystemService { return dispatchMessageToLocalDevice(message); } + private boolean dispatchMessageToAction(HdmiCecMessage message) { + for (FeatureAction action : mActions) { + if (action.processCommand(message)) { + return true; + } + } + return false; + } + private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { if (device.dispatchMessage(message)) { return true; } } + + Slog.w(TAG, "Unhandled cec command:" + message); return false; } @@ -589,7 +638,18 @@ public final class HdmiControlService extends SystemService { * @param connected whether to be plugged in or not */ void onHotplug(int portNo, boolean connected) { - // TODO: Start "RequestArcInitiationAction" if ARC port. + assertRunOnServiceThread(); + // TODO: delegate onHotplug event to each local device. + + // Tv device will have permanent HotplugDetectionAction. + List hotplugActions = getActions(HotplugDetectionAction.class); + if (!hotplugActions.isEmpty()) { + // Note that hotplug action is single action running on a machine. + // "pollAllDevicesNow" cleans up timer and start poll action immediately. + hotplugActions.get(0).pollAllDevicesNow(); + } + + announceHotplugEvent(portNo, connected); } /** @@ -617,35 +677,32 @@ public final class HdmiControlService extends SystemService { return strategy | iterationStrategy; } + void clearAllDeviceInfo() { + assertRunOnServiceThread(); + mCecController.clearDeviceInfoList(); + } + + List getAllLocalDevices() { + assertRunOnServiceThread(); + return mCecController.getLocalDeviceList(); + } + /** - * Launch device discovery sequence. It starts with clearing the existing device info list. - * Note that it assumes that logical address of all local devices is already allocated. + * 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 sourceAddress a logical address of tv + * @param physicalAddress physical address of a device to be searched + * @param logicalAddress logical address of a device to be searched + * @return true if exist; otherwise false */ - void launchDeviceDiscovery(final int sourceAddress) { - // At first, clear all existing device infos. - mCecController.clearDeviceInfoList(); - // TODO: flush cec message cache when CEC is turned off. - - DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress, - new DeviceDiscoveryCallback() { - @Override - public void onDeviceDiscoveryDone(List deviceInfos) { - for (HdmiCecDeviceInfo info : deviceInfos) { - addCecDevice(info); - } - - // Add device info of all local devices. - for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { - addCecDevice(device.getDeviceInfo()); - } - - addAndStartAction(new HotplugDetectionAction(HdmiControlService.this, - sourceAddress)); - } - }); - addAndStartAction(action); + boolean isInDeviceList(int physicalAddress, int logicalAddress) { + assertRunOnServiceThread(); + HdmiCecDeviceInfo device = mCecController.getDeviceInfo(logicalAddress); + if (device == null) { + return false; + } + return device.getPhysicalAddress() == physicalAddress; } private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { @@ -675,16 +732,6 @@ public final class HdmiControlService extends SystemService { addAndStartAction(action); } - private boolean dispatchMessageToAction(HdmiCecMessage message) { - for (FeatureAction action : mActions) { - if (action.processCommand(message)) { - return true; - } - } - Slog.w(TAG, "Unsupported cec command:" + message); - return false; - } - private void handleSetSystemAudioMode(HdmiCecMessage message) { if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) { return; @@ -730,10 +777,6 @@ public final class HdmiControlService extends SystemService { } } - void addCecDevice(HdmiCecDeviceInfo info) { - mCecController.addDeviceInfo(info); - } - private void enforceAccessPermission() { getContext().enforceCallingOrSelfPermission(PERMISSION, TAG); } @@ -768,7 +811,6 @@ public final class HdmiControlService extends SystemService { }); } - @Override public void oneTouchPlay(final IHdmiControlCallback callback) { enforceAccessPermission(); @@ -910,6 +952,17 @@ public final class HdmiControlService extends SystemService { } } + /** + * Called when a device is newly added or a new device is detected. + * + * @param info device info of a new device. + */ + void addCecDevice(HdmiCecDeviceInfo info) { + mCecController.addDeviceInfo(info); + + // TODO: announce new device detection. + } + /** * Called when a device is removed or removal of device is detected. * @@ -918,9 +971,61 @@ public final class HdmiControlService extends SystemService { void removeCecDevice(int address) { mCecController.removeDeviceInfo(address); mCecMessageCache.flushMessagesFrom(address); + + // TODO: announce a device removal. + } + + private void announceHotplugEvent(int portNo, boolean connected) { + HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected); + synchronized (mLock) { + for (IHdmiHotplugEventListener listener : mHotplugEventListeners) { + invokeHotplugEventListener(listener, event); + } + } + } + + private void invokeHotplugEventListener(IHdmiHotplugEventListener listener, + HdmiHotplugEvent event) { + try { + listener.onReceived(event); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report hotplug event:" + event.toString(), e); + } } HdmiCecMessageCache getCecMessageCache() { return mCecMessageCache; } + + private static boolean hasSameTopPort(int path1, int path2) { + return (path1 & HdmiConstants.ROUTING_PATH_TOP_MASK) + == (path2 & HdmiConstants.ROUTING_PATH_TOP_MASK); + } + + /** + * Whether the given path is located in the tail of current active path. + * + * @param path to be tested + * @return true if the given path is located in the tail of current active path; otherwise, + * false + */ + // TODO: move this to local device tv. + boolean isTailOfActivePath(int path) { + // If active routing path is internal source, return false. + if (mActiveRoutingPath == 0) { + return false; + } + for (int i = 12; i >= 0; i -= 4) { + int curActivePath = (mActiveRoutingPath >> i) & 0xF; + if (curActivePath == 0) { + return true; + } else { + int curPath = (path >> i) & 0xF; + if (curPath != curActivePath) { + return false; + } + } + } + return false; + } } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index c7a813d128ab..ae20eda99e9e 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -90,6 +90,20 @@ final class HotplugDetectionAction extends FeatureAction { } } + /** + * Start device polling immediately. + */ + void pollAllDevicesNow() { + // Clear existing timer to avoid overlapped execution + mActionTimer.clearTimerMessage(); + + mTimeoutCount = 0; + mState = STATE_WAIT_FOR_NEXT_POLLING; + pollAllDevices(); + + addTimer(mState, POLLING_INTERVAL_MS); + } + // This method is called every 5 seconds. private void pollDevices() { // All device check called every 15 seconds. @@ -180,15 +194,56 @@ final class HotplugDetectionAction extends FeatureAction { } private void addDevice(int addedAddress) { - // TODO: implement this. + // Send . + sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, addedAddress)); } private void removeDevice(int removedAddress) { + // TODO: move the following logic to local device once move many logic to + // local device. + mayChangeRoutingPath(removedAddress); + mayCancelDeviceSelect(removedAddress); + mayCancelOneTouchRecord(removedAddress); + mayDisableSystemAudioAndARC(removedAddress); + mService.removeCecDevice(removedAddress); - // TODO: implements following steps. - // 1. Launch routing control sequence - // 2. Stop one touch play sequence if removed device is the device to be selected. - // 3. If audio system, start system audio off and arc off - // 4. Inform device remove to others + } + + private void mayChangeRoutingPath(int address) { + // TODO: if removed address is current active source, it should change active source + // path new one. we can keep previous selection or can choose default input, + // such as internal tuner. Consider send intent to app so that app + // can handle it. + } + + private void mayCancelDeviceSelect(int address) { + List actions = mService.getActions(DeviceSelectAction.class); + if (actions.isEmpty()) { + return; + } + + // Should ave only one Device Select Action + DeviceSelectAction action = actions.get(0); + if (action.getTargetAddress() == address) { + mService.removeAction(DeviceSelectAction.class); + } + } + + private void mayCancelOneTouchRecord(int address) { + // TODO: implement this. + } + + private void mayDisableSystemAudioAndARC(int address) { + if (HdmiCec.getTypeFromAddress(address) != HdmiCec.DEVICE_AUDIO_SYSTEM) { + return; + } + + // Turn off system audio mode. + mService.setSystemAudioMode(false); + if (mService.getArcStatus()) { + mService.addAndStartAction( + new RequestArcTerminationAction(mService, mSourceAddress, address)); + } + } } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index c284d10c4ba7..2cae507797b4 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -48,6 +48,7 @@ final class NewDeviceAction extends FeatureAction { private final int mDeviceLogicalAddress; private final int mDevicePhysicalAddress; + private final boolean mRequireRoutingChange; private int mVendorId; private String mDisplayName; @@ -59,17 +60,36 @@ final class NewDeviceAction extends FeatureAction { * @param sourceAddress logical address to be used as source address * @param deviceLogicalAddress logical address of the device in interest * @param devicePhysicalAddress physical address of the device in interest + * @param requireRoutingChange whether to initiate routing change or not */ NewDeviceAction(HdmiControlService service, int sourceAddress, int deviceLogicalAddress, - int devicePhysicalAddress) { + int devicePhysicalAddress, boolean requireRoutingChange) { super(service, sourceAddress); mDeviceLogicalAddress = deviceLogicalAddress; mDevicePhysicalAddress = devicePhysicalAddress; mVendorId = HdmiCec.UNKNOWN_VENDOR_ID; + mRequireRoutingChange = requireRoutingChange; } @Override public boolean start() { + if (HdmiCec.getTypeFromAddress(mSourceAddress) == HdmiCec.DEVICE_AUDIO_SYSTEM) { + if (mService.getAvrDeviceInfo() == null) { + // TODO: Start system audio initiation action + } + + // If new device is connected through ARC enabled port, + // initiates ARC channel establishment. + if (mService.isConnectedToArcPort(mDevicePhysicalAddress)) { + mService.addAndStartAction(new RequestArcInitiationAction(mService, mSourceAddress, + mDeviceLogicalAddress)); + } + } + + if (mRequireRoutingChange) { + startRoutingChange(); + } + mState = STATE_WAITING_FOR_SET_OSD_NAME; if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_SET_OSD_NAME)) { return true; @@ -133,6 +153,22 @@ final class NewDeviceAction extends FeatureAction { return false; } + private void startRoutingChange() { + // Stop existing routing control. + mService.removeAction(RoutingControlAction.class); + + // Send routing change. The the address is a path of the active port. + int newPath = toTopMostPortPath(mDevicePhysicalAddress); + sendCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress, + mService.getActivePath(), newPath)); + mService.addAndStartAction(new RoutingControlAction(mService, mSourceAddress, + mService.pathToPortId(newPath), null)); + } + + private static int toTopMostPortPath(int physicalAddress) { + return physicalAddress & HdmiConstants.ROUTING_PATH_TOP_MASK; + } + private boolean mayProcessCommandIfCached(int destAddress, int opcode) { HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode); if (message != null) { diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java index 65615a48543b..19974ea6d418 100644 --- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -16,12 +16,11 @@ package com.android.server.hdmi; -import java.util.concurrent.TimeUnit; - -import android.hardware.hdmi.IHdmiControlCallback; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.IHdmiControlCallback; import android.os.RemoteException; import android.util.Slog; @@ -59,7 +58,7 @@ public class RoutingControlAction extends FeatureAction { // Time out in milliseconds used for private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000; - private final IHdmiControlCallback mCallback; + @Nullable private final IHdmiControlCallback mCallback; // The latest routing path. Updated by each from CEC switches. private int mCurrentRoutingPath; @@ -167,7 +166,7 @@ public class RoutingControlAction extends FeatureAction { case STATE_WAIT_FOR_ROUTING_INFORMATION: HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath); if (device == null) { - maybeChangeActiveInput(mCurrentRoutingPath); + maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath)); } else { // TODO: Also check followings and then proceed: // if routing change was neither triggered by TV at CEC enable time, nor @@ -185,7 +184,7 @@ public class RoutingControlAction extends FeatureAction { case STATE_WAIT_FOR_REPORT_POWER_STATUS: int tvPowerStatus = getTvPowerStatus(); if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) { - if (!maybeChangeActiveInput(mCurrentRoutingPath)) { + if (!maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath))) { sendSetStreamPath(); } } @@ -217,15 +216,10 @@ public class RoutingControlAction extends FeatureAction { mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS); } else { - maybeChangeActiveInput(mCurrentRoutingPath); + maybeChangeActiveInput(mService.pathToPortId(mCurrentRoutingPath)); } } - // Given the HDMI port id, return the port address. - private int portToPath(int portId) { - return mService.getPortInfo(portId).getAddress(); - } - private void invokeCallback(int result) { if (mCallback == null) { return; -- cgit v1.2.3-59-g8ed1b