Loading core/java/android/hardware/hdmi/HdmiCec.java +3 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ public final class HdmiCec { /** Logical address used to indicate it is not initialized or invalid. */ public static final int ADDR_INVALID = -1; /** Logical address used to indicate the source comes from internal device. */ public static final int ADDR_INTERNAL = 0xFFFF; // TODO: Complete the list of CEC messages definition. public static final int MESSAGE_FEATURE_ABORT = 0x00; public static final int MESSAGE_IMAGE_VIEW_ON = 0x04; Loading services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +26 −28 Original line number Diff line number Diff line Loading @@ -27,18 +27,18 @@ import android.util.Slog; /** * Handles CEC command <Active Source>. * <p> * Used by feature actions that need to handle the command in their flow. * Used by feature actions that need to handle the command in their flow. Only for TV * local device. */ final class ActiveSourceHandler { private static final String TAG = "ActiveSourceHandler"; private final HdmiCecLocalDevice mSource; private final HdmiCecLocalDeviceTv mSource; private final HdmiControlService mService; @Nullable private final IHdmiControlCallback mCallback; static ActiveSourceHandler create(HdmiCecLocalDevice source, IHdmiControlCallback callback) { static ActiveSourceHandler create(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) { if (source == null) { Slog.e(TAG, "Wrong arguments"); return null; Loading @@ -46,7 +46,7 @@ final class ActiveSourceHandler { return new ActiveSourceHandler(source, callback); } private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) { private ActiveSourceHandler(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) { mSource = source; mService = mSource.getService(); mCallback = callback; Loading @@ -55,48 +55,46 @@ final class ActiveSourceHandler { /** * Handles the incoming active source command. * * @param deviceLogicalAddress logical address of the device to be the active source * @param routingPath routing path of the device to be the active source * @param activeAddress logical address of the device to be the active source * @param activePath routing path of the device to be the active source */ void process(int deviceLogicalAddress, int routingPath) { if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) { void process(int activeAddress, int activePath) { // Seq #17 HdmiCecLocalDeviceTv tv = mSource; if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) { invokeCallback(HdmiCec.RESULT_SUCCESS); return; } HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress); HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress); if (device == null) { // "New device action" initiated by <Active Source> does not require // "Routing change action". mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress, routingPath, false)); tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath, false)); } if (!mSource.isInPresetInstallationMode()) { int prevActiveInput = mSource.getActivePortId(); mSource.updateActiveDevice(deviceLogicalAddress, routingPath); if (prevActiveInput != mSource.getActivePortId()) { // TODO: change port input here. int currentActive = tv.getActiveSource(); int currentPath = tv.getActivePath(); if (!tv.isInPresetInstallationMode()) { tv.updateActiveSource(activeAddress, activePath); if (currentActive != activeAddress && currentPath != activePath) { tv.updateActivePortId(mService.pathToPortId(activePath)); } invokeCallback(HdmiCec.RESULT_SUCCESS); } else { // TV is in a mode that should keep its current source/input from // being changed for its operation. Reclaim the active source // or switch the port back to the one used for the current mode. if (mSource.getActiveSource() == getSourceAddress()) { if (currentActive == getSourceAddress()) { HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()); HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath); mService.sendCecCommand(activeSource); mSource.updateActiveDevice(deviceLogicalAddress, routingPath); tv.updateActiveSource(currentActive, currentPath); invokeCallback(HdmiCec.RESULT_SUCCESS); } else { int activePath = mSource.getActivePath(); mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(), routingPath, activePath)); // TODO: Start port select action here // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(), // activePath, mCallback); // mService.addActionAndStart(action); HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange( getSourceAddress(), activePath, currentPath); mService.sendCecCommand(routingChange); tv.addAndStartAction(new RoutingControlAction(tv, currentPath, mCallback)); } } } Loading services/core/java/com/android/server/hdmi/DeviceSelectAction.java +2 −2 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ final class DeviceSelectAction extends FeatureAction { * @param target target logical device that will be a new active source * @param callback callback object */ public DeviceSelectAction(HdmiCecLocalDevice source, public DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiCecDeviceInfo target, IHdmiControlCallback callback) { super(source); mCallback = callback; Loading Loading @@ -116,7 +116,7 @@ final class DeviceSelectAction extends FeatureAction { if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) { int activePath = HdmiUtils.twoBytesToInt(params); ActiveSourceHandler .create(localDevice(), mCallback) .create((HdmiCecLocalDeviceTv) localDevice(), mCallback) .process(cmd.getSource(), activePath); finish(); return true; Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +40 −3 Original line number Diff line number Diff line Loading @@ -125,6 +125,12 @@ abstract class HdmiCecLocalDevice { return true; } switch (message.getOpcode()) { case HdmiCec.MESSAGE_ACTIVE_SOURCE: return handleActiveSource(message); case HdmiCec.MESSAGE_INACTIVE_SOURCE: return handleInactiveSource(message); case HdmiCec.MESSAGE_REQUEST_ACTIVE_SOURCE: return handleRequestActiveSource(message); case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: return handleGetMenuLanguage(message); case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: Loading Loading @@ -192,6 +198,21 @@ abstract class HdmiCecLocalDevice { return true; } @ServiceThreadOnly protected boolean handleActiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleInactiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleRequestActiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleGetMenuLanguage(HdmiCecMessage message) { assertRunOnServiceThread(); Loading Loading @@ -383,15 +404,24 @@ abstract class HdmiCecLocalDevice { } } /** * Returns the active routing path. */ void setActiveSource(int source) { synchronized (mLock) { mActiveSource = source; } } int getActivePath() { synchronized (mLock) { return mActiveRoutingPath; } } void setActivePath(int path) { synchronized (mLock) { mActiveRoutingPath = path; } } /** * Returns the ID of the active HDMI port. The active port is the one that has the active * routing path connected to it directly or indirectly under the device hierarchy. Loading Loading @@ -429,6 +459,13 @@ abstract class HdmiCecLocalDevice { } boolean isInPresetInstallationMode() { // TODO: Change this to check the right flag. synchronized (mLock) { return !mInputChangeEnabled; } } boolean isHdmiControlEnabled() { synchronized (mLock) { return !mInputChangeEnabled; } Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +136 −14 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @GuardedBy("mLock") private boolean mSystemAudioMode; // The previous port id (input) before switching to the new one. This is remembered in order to // be able to switch to it upon receiving <Inactive Source> from currently active source. // This remains valid only when the active source was switched via one touch play operation // (either by TV or source device). Manual port switching invalidates this value to // HdmiConstants.PORT_INVALID, for which case <Inactive Source> does not do anything. @GuardedBy("mLock") private int mPrevPortId; // Copy of mDeviceInfos to guarantee thread-safety. @GuardedBy("mLock") private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList(); Loading @@ -62,7 +70,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiCec.DEVICE_TV); mPrevPortId = HdmiConstants.INVALID_PORT_ID; // TODO: load system audio mode and set it to mSystemAudioMode. } Loading Loading @@ -90,6 +98,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void deviceSelect(int targetAddress, IHdmiControlCallback callback) { assertRunOnServiceThread(); if (targetAddress == HdmiCec.ADDR_INTERNAL) { handleSelectInternalSource(callback); return; } HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress); if (targetDevice == null) { invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE); Loading @@ -99,28 +111,83 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { addAndStartAction(new DeviceSelectAction(this, targetDevice, callback)); } @ServiceThreadOnly private void handleSelectInternalSource(IHdmiControlCallback callback) { assertRunOnServiceThread(); // Seq #18 if (isHdmiControlEnabled() && getActiveSource() != mAddress) { updateActiveSource(mAddress, mService.getPhysicalAddress()); // TODO: Check if this comes from <Text/Image View On> - if true, do nothing. HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( mAddress, mService.getPhysicalAddress()); mService.sendCecCommand(activeSource); } } @ServiceThreadOnly void updateActiveSource(int activeSource, int activePath) { assertRunOnServiceThread(); // Seq #14 if (activeSource == getActiveSource() && activePath == getActivePath()) { return; } setActiveSource(activeSource); setActivePath(activePath); if (getDeviceInfo(activeSource) != null && activeSource != mAddress) { if (mService.pathToPortId(activePath) == getActivePortId()) { setPrevPortId(getActivePortId()); } // TODO: Show the OSD banner related to the new active source device. } else { // TODO: If displayed, remove the OSD banner related to the previous // active source device. } } /** * Performs the action routing control. * * @param portId new HDMI port to route to * @param callback callback object to report the result with * Returns the previous port id kept to handle input switching on <Inactive Source>. */ int getPrevPortId() { synchronized (mLock) { return mPrevPortId; } } /** * Sets the previous port id. INVALID_PORT_ID invalidates it, hence no actions will be * taken for <Inactive Source>. */ void setPrevPortId(int portId) { synchronized (mLock) { mPrevPortId = portId; } } @ServiceThreadOnly void portSelect(int portId, IHdmiControlCallback callback) { void updateActivePortId(int portId) { assertRunOnServiceThread(); if (isInPresetInstallationMode()) { invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE); // Seq #15 if (portId == getActivePortId()) { return; } // Make sure this call does not stem from <Active Source> message reception, in // which case the two ports will be the same. if (portId == getActivePortId()) { invokeCallback(callback, HdmiCec.RESULT_SUCCESS); setPrevPortId(portId); // TODO: Actually switch the physical port here. Handle PAP/PIP as well. // Show OSD port change banner } @ServiceThreadOnly void doManualPortSwitching(int portId, IHdmiControlCallback callback) { assertRunOnServiceThread(); // Seq #20 if (!isHdmiControlEnabled() || portId == getActivePortId()) { invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE); return; } setActivePortId(portId); // TODO: Make sure this call does not stem from <Active Source> message reception. setActivePortId(portId); // TODO: Return immediately if the operation is triggered by <Text/Image View On> // and this is the first notification about the active input after power-on. // TODO: Handle invalid port id / active input which should be treated as an // internal tuner. Loading Loading @@ -166,6 +233,61 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } @Override @ServiceThreadOnly protected boolean handleActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); int activePath = HdmiUtils.twoBytesToInt(message.getParams()); ActiveSourceHandler.create(this, null).process(message.getSource(), activePath); return true; } @Override @ServiceThreadOnly protected boolean handleInactiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); // Seq #10 // Ignore <Inactive Source> from non-active source device. if (getActiveSource() != message.getSource()) { return true; } if (isInPresetInstallationMode()) { return true; } int portId = getPrevPortId(); if (portId != HdmiConstants.INVALID_PORT_ID) { // TODO: Do this only if TV is not showing multiview like PIP/PAP. HdmiCecDeviceInfo inactiveSource = getDeviceInfo(message.getSource()); if (inactiveSource == null) { return true; } if (mService.pathToPortId(inactiveSource.getPhysicalAddress()) == portId) { return true; } // TODO: Switch the TV freeze mode off setActivePortId(portId); doManualPortSwitching(portId, null); setPrevPortId(HdmiConstants.INVALID_PORT_ID); } return true; } @Override @ServiceThreadOnly protected boolean handleRequestActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); // Seq #19 int address = getDeviceInfo().getLogicalAddress(); if (address == getActiveSource()) { mService.sendCecCommand( HdmiCecMessageBuilder.buildActiveSource(address, getActivePath())); } return true; } @Override @ServiceThreadOnly protected boolean handleGetMenuLanguage(HdmiCecMessage message) { Loading Loading
core/java/android/hardware/hdmi/HdmiCec.java +3 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,9 @@ public final class HdmiCec { /** Logical address used to indicate it is not initialized or invalid. */ public static final int ADDR_INVALID = -1; /** Logical address used to indicate the source comes from internal device. */ public static final int ADDR_INTERNAL = 0xFFFF; // TODO: Complete the list of CEC messages definition. public static final int MESSAGE_FEATURE_ABORT = 0x00; public static final int MESSAGE_IMAGE_VIEW_ON = 0x04; Loading
services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +26 −28 Original line number Diff line number Diff line Loading @@ -27,18 +27,18 @@ import android.util.Slog; /** * Handles CEC command <Active Source>. * <p> * Used by feature actions that need to handle the command in their flow. * Used by feature actions that need to handle the command in their flow. Only for TV * local device. */ final class ActiveSourceHandler { private static final String TAG = "ActiveSourceHandler"; private final HdmiCecLocalDevice mSource; private final HdmiCecLocalDeviceTv mSource; private final HdmiControlService mService; @Nullable private final IHdmiControlCallback mCallback; static ActiveSourceHandler create(HdmiCecLocalDevice source, IHdmiControlCallback callback) { static ActiveSourceHandler create(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) { if (source == null) { Slog.e(TAG, "Wrong arguments"); return null; Loading @@ -46,7 +46,7 @@ final class ActiveSourceHandler { return new ActiveSourceHandler(source, callback); } private ActiveSourceHandler(HdmiCecLocalDevice source, IHdmiControlCallback callback) { private ActiveSourceHandler(HdmiCecLocalDeviceTv source, IHdmiControlCallback callback) { mSource = source; mService = mSource.getService(); mCallback = callback; Loading @@ -55,48 +55,46 @@ final class ActiveSourceHandler { /** * Handles the incoming active source command. * * @param deviceLogicalAddress logical address of the device to be the active source * @param routingPath routing path of the device to be the active source * @param activeAddress logical address of the device to be the active source * @param activePath routing path of the device to be the active source */ void process(int deviceLogicalAddress, int routingPath) { if (getSourcePath() == routingPath && mSource.getActiveSource() == getSourceAddress()) { void process(int activeAddress, int activePath) { // Seq #17 HdmiCecLocalDeviceTv tv = mSource; if (getSourcePath() == activePath && tv.getActiveSource() == getSourceAddress()) { invokeCallback(HdmiCec.RESULT_SUCCESS); return; } HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress); HdmiCecDeviceInfo device = mService.getDeviceInfo(activeAddress); if (device == null) { // "New device action" initiated by <Active Source> does not require // "Routing change action". mSource.addAndStartAction(new NewDeviceAction(mSource, deviceLogicalAddress, routingPath, false)); tv.addAndStartAction(new NewDeviceAction(tv, activeAddress, activePath, false)); } if (!mSource.isInPresetInstallationMode()) { int prevActiveInput = mSource.getActivePortId(); mSource.updateActiveDevice(deviceLogicalAddress, routingPath); if (prevActiveInput != mSource.getActivePortId()) { // TODO: change port input here. int currentActive = tv.getActiveSource(); int currentPath = tv.getActivePath(); if (!tv.isInPresetInstallationMode()) { tv.updateActiveSource(activeAddress, activePath); if (currentActive != activeAddress && currentPath != activePath) { tv.updateActivePortId(mService.pathToPortId(activePath)); } invokeCallback(HdmiCec.RESULT_SUCCESS); } else { // TV is in a mode that should keep its current source/input from // being changed for its operation. Reclaim the active source // or switch the port back to the one used for the current mode. if (mSource.getActiveSource() == getSourceAddress()) { if (currentActive == getSourceAddress()) { HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()); HdmiCecMessageBuilder.buildActiveSource(currentActive, currentPath); mService.sendCecCommand(activeSource); mSource.updateActiveDevice(deviceLogicalAddress, routingPath); tv.updateActiveSource(currentActive, currentPath); invokeCallback(HdmiCec.RESULT_SUCCESS); } else { int activePath = mSource.getActivePath(); mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(getSourceAddress(), routingPath, activePath)); // TODO: Start port select action here // PortSelectAction action = new PortSelectAction(mService, getSourceAddress(), // activePath, mCallback); // mService.addActionAndStart(action); HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange( getSourceAddress(), activePath, currentPath); mService.sendCecCommand(routingChange); tv.addAndStartAction(new RoutingControlAction(tv, currentPath, mCallback)); } } } Loading
services/core/java/com/android/server/hdmi/DeviceSelectAction.java +2 −2 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ final class DeviceSelectAction extends FeatureAction { * @param target target logical device that will be a new active source * @param callback callback object */ public DeviceSelectAction(HdmiCecLocalDevice source, public DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiCecDeviceInfo target, IHdmiControlCallback callback) { super(source); mCallback = callback; Loading Loading @@ -116,7 +116,7 @@ final class DeviceSelectAction extends FeatureAction { if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) { int activePath = HdmiUtils.twoBytesToInt(params); ActiveSourceHandler .create(localDevice(), mCallback) .create((HdmiCecLocalDeviceTv) localDevice(), mCallback) .process(cmd.getSource(), activePath); finish(); return true; Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +40 −3 Original line number Diff line number Diff line Loading @@ -125,6 +125,12 @@ abstract class HdmiCecLocalDevice { return true; } switch (message.getOpcode()) { case HdmiCec.MESSAGE_ACTIVE_SOURCE: return handleActiveSource(message); case HdmiCec.MESSAGE_INACTIVE_SOURCE: return handleInactiveSource(message); case HdmiCec.MESSAGE_REQUEST_ACTIVE_SOURCE: return handleRequestActiveSource(message); case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: return handleGetMenuLanguage(message); case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: Loading Loading @@ -192,6 +198,21 @@ abstract class HdmiCecLocalDevice { return true; } @ServiceThreadOnly protected boolean handleActiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleInactiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleRequestActiveSource(HdmiCecMessage message) { return false; } @ServiceThreadOnly protected boolean handleGetMenuLanguage(HdmiCecMessage message) { assertRunOnServiceThread(); Loading Loading @@ -383,15 +404,24 @@ abstract class HdmiCecLocalDevice { } } /** * Returns the active routing path. */ void setActiveSource(int source) { synchronized (mLock) { mActiveSource = source; } } int getActivePath() { synchronized (mLock) { return mActiveRoutingPath; } } void setActivePath(int path) { synchronized (mLock) { mActiveRoutingPath = path; } } /** * Returns the ID of the active HDMI port. The active port is the one that has the active * routing path connected to it directly or indirectly under the device hierarchy. Loading Loading @@ -429,6 +459,13 @@ abstract class HdmiCecLocalDevice { } boolean isInPresetInstallationMode() { // TODO: Change this to check the right flag. synchronized (mLock) { return !mInputChangeEnabled; } } boolean isHdmiControlEnabled() { synchronized (mLock) { return !mInputChangeEnabled; } Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +136 −14 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @GuardedBy("mLock") private boolean mSystemAudioMode; // The previous port id (input) before switching to the new one. This is remembered in order to // be able to switch to it upon receiving <Inactive Source> from currently active source. // This remains valid only when the active source was switched via one touch play operation // (either by TV or source device). Manual port switching invalidates this value to // HdmiConstants.PORT_INVALID, for which case <Inactive Source> does not do anything. @GuardedBy("mLock") private int mPrevPortId; // Copy of mDeviceInfos to guarantee thread-safety. @GuardedBy("mLock") private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList(); Loading @@ -62,7 +70,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiCec.DEVICE_TV); mPrevPortId = HdmiConstants.INVALID_PORT_ID; // TODO: load system audio mode and set it to mSystemAudioMode. } Loading Loading @@ -90,6 +98,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void deviceSelect(int targetAddress, IHdmiControlCallback callback) { assertRunOnServiceThread(); if (targetAddress == HdmiCec.ADDR_INTERNAL) { handleSelectInternalSource(callback); return; } HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress); if (targetDevice == null) { invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE); Loading @@ -99,28 +111,83 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { addAndStartAction(new DeviceSelectAction(this, targetDevice, callback)); } @ServiceThreadOnly private void handleSelectInternalSource(IHdmiControlCallback callback) { assertRunOnServiceThread(); // Seq #18 if (isHdmiControlEnabled() && getActiveSource() != mAddress) { updateActiveSource(mAddress, mService.getPhysicalAddress()); // TODO: Check if this comes from <Text/Image View On> - if true, do nothing. HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( mAddress, mService.getPhysicalAddress()); mService.sendCecCommand(activeSource); } } @ServiceThreadOnly void updateActiveSource(int activeSource, int activePath) { assertRunOnServiceThread(); // Seq #14 if (activeSource == getActiveSource() && activePath == getActivePath()) { return; } setActiveSource(activeSource); setActivePath(activePath); if (getDeviceInfo(activeSource) != null && activeSource != mAddress) { if (mService.pathToPortId(activePath) == getActivePortId()) { setPrevPortId(getActivePortId()); } // TODO: Show the OSD banner related to the new active source device. } else { // TODO: If displayed, remove the OSD banner related to the previous // active source device. } } /** * Performs the action routing control. * * @param portId new HDMI port to route to * @param callback callback object to report the result with * Returns the previous port id kept to handle input switching on <Inactive Source>. */ int getPrevPortId() { synchronized (mLock) { return mPrevPortId; } } /** * Sets the previous port id. INVALID_PORT_ID invalidates it, hence no actions will be * taken for <Inactive Source>. */ void setPrevPortId(int portId) { synchronized (mLock) { mPrevPortId = portId; } } @ServiceThreadOnly void portSelect(int portId, IHdmiControlCallback callback) { void updateActivePortId(int portId) { assertRunOnServiceThread(); if (isInPresetInstallationMode()) { invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE); // Seq #15 if (portId == getActivePortId()) { return; } // Make sure this call does not stem from <Active Source> message reception, in // which case the two ports will be the same. if (portId == getActivePortId()) { invokeCallback(callback, HdmiCec.RESULT_SUCCESS); setPrevPortId(portId); // TODO: Actually switch the physical port here. Handle PAP/PIP as well. // Show OSD port change banner } @ServiceThreadOnly void doManualPortSwitching(int portId, IHdmiControlCallback callback) { assertRunOnServiceThread(); // Seq #20 if (!isHdmiControlEnabled() || portId == getActivePortId()) { invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE); return; } setActivePortId(portId); // TODO: Make sure this call does not stem from <Active Source> message reception. setActivePortId(portId); // TODO: Return immediately if the operation is triggered by <Text/Image View On> // and this is the first notification about the active input after power-on. // TODO: Handle invalid port id / active input which should be treated as an // internal tuner. Loading Loading @@ -166,6 +233,61 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } @Override @ServiceThreadOnly protected boolean handleActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); int activePath = HdmiUtils.twoBytesToInt(message.getParams()); ActiveSourceHandler.create(this, null).process(message.getSource(), activePath); return true; } @Override @ServiceThreadOnly protected boolean handleInactiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); // Seq #10 // Ignore <Inactive Source> from non-active source device. if (getActiveSource() != message.getSource()) { return true; } if (isInPresetInstallationMode()) { return true; } int portId = getPrevPortId(); if (portId != HdmiConstants.INVALID_PORT_ID) { // TODO: Do this only if TV is not showing multiview like PIP/PAP. HdmiCecDeviceInfo inactiveSource = getDeviceInfo(message.getSource()); if (inactiveSource == null) { return true; } if (mService.pathToPortId(inactiveSource.getPhysicalAddress()) == portId) { return true; } // TODO: Switch the TV freeze mode off setActivePortId(portId); doManualPortSwitching(portId, null); setPrevPortId(HdmiConstants.INVALID_PORT_ID); } return true; } @Override @ServiceThreadOnly protected boolean handleRequestActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); // Seq #19 int address = getDeviceInfo().getLogicalAddress(); if (address == getActiveSource()) { mService.sendCecCommand( HdmiCecMessageBuilder.buildActiveSource(address, getActivePath())); } return true; } @Override @ServiceThreadOnly protected boolean handleGetMenuLanguage(HdmiCecMessage message) { Loading