Loading services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java 0 → 100644 +133 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import java.util.ArrayList; import java.util.Iterator; /** * Buffer storage to keep incoming messages for later processing. Used to * handle messages that arrive when the device is not ready. Useful when * keeping the messages from a connected device which are not discovered yet. */ final class DelayedMessageBuffer { private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>(); private final HdmiCecLocalDevice mDevice; DelayedMessageBuffer(HdmiCecLocalDevice device) { mDevice = device; } /** * Add a new message to the buffer. The buffer keeps selected messages in * the order they are received. * * @param message {@link HdmiCecMessage} to add */ void add(HdmiCecMessage message) { boolean buffered = true; // Note that all the messages are not handled in the same manner. // For <Active Source> we keep the latest one only. // TODO: This might not be the best way to choose the active source. // Devise a better way to pick up the best one. switch (message.getOpcode()) { case Constants.MESSAGE_ACTIVE_SOURCE: removeActiveSource(); mBuffer.add(message); break; case Constants.MESSAGE_INITIATE_ARC: mBuffer.add(message); break; default: buffered = false; break; } if (buffered) { HdmiLogger.debug("Buffering message:" + message); } } private void removeActiveSource() { // Uses iterator to remove elements while looping through the list. for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { iter.remove(); } } } void processAllMessages() { for (HdmiCecMessage message : mBuffer) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); } mBuffer.clear(); } /** * Process messages from a given logical device. Called by * {@link NewDeviceAction} actions when they finish adding the device * information. * <p><Active Source> is not processed in this method but processed * separately via {@link #processActiveSource()}. * * @param address logical address of CEC device which the messages to process * are associated with */ void processMessagesForDevice(int address) { HdmiLogger.debug("Processing message for address:" + address); for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { continue; } if (message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); iter.remove(); } } } /** * Process <Active Source>. * * <p>The message has a dependency on TV input framework. Should be invoked * after we get the callback * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)} * to ensure the processing of the message takes effect when transformed * to input change callback. * * @param address logical address of the device to be the active source */ void processActiveSource(int address) { for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE && message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); iter.remove(); } } } } services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -326,6 +326,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { Slog.v(TAG, "--------------------------------------------"); Slog.v(TAG, "--------------------------------------------"); mCallback.onDeviceDiscoveryDone(result); mCallback.onDeviceDiscoveryDone(result); finish(); finish(); // Process any commands buffered while device discovery action was in progress. tv().processAllDelayedMessages(); } } private void checkAndProceedStage() { private void checkAndProceedStage() { Loading services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +55 −4 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,8 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; import android.annotation.Nullable; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiRecordSources; import android.hardware.hdmi.HdmiRecordSources; Loading @@ -37,6 +39,9 @@ import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback; import android.media.AudioManager; import android.media.AudioManager; import android.media.AudioSystem; import android.media.AudioSystem; import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.os.RemoteException; import android.os.RemoteException; import android.os.SystemProperties; import android.os.SystemProperties; import android.provider.Settings.Global; import android.provider.Settings.Global; Loading @@ -49,6 +54,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.SystemService; import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -123,6 +129,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // other CEC devices since they might not have logical address. // other CEC devices since they might not have logical address. private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>(); private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>(); // Message buffer used to buffer selected messages to process later. <Active Source> // from a source device, for instance, needs to be buffered if the device is not // discovered yet. The buffered commands are taken out and when they are ready to // handle. private final DelayedMessageBuffer mDelayedMessageBuffer = new DelayedMessageBuffer(this); // Defines the callback invoked when TV input framework is updated with input status. // We are interested in the notification for HDMI input addition event, in order to // process any CEC commands that arrived before the input is added. private final TvInputCallback mTvInputCallback = new TvInputCallback() { @Override public void onInputAdded(String inputId) { TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); if (info != null && info.isCecDevice()) { mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress()); } } }; HdmiCecLocalDeviceTv(HdmiControlService service) { HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_TV); super(service, HdmiDeviceInfo.DEVICE_TV); mPrevPortId = Constants.INVALID_PORT_ID; mPrevPortId = Constants.INVALID_PORT_ID; Loading @@ -136,6 +162,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); assertRunOnServiceThread(); mService.registerTvInputCallback(mTvInputCallback); mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); mAddress, mService.getPhysicalAddress(), mDeviceType)); mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( Loading Loading @@ -407,7 +434,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { if (info == null) { handleNewDeviceAtTheTailOfActivePath(physicalAddress); if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) { mDelayedMessageBuffer.add(message); } } else { } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); Loading Loading @@ -555,7 +584,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { activeSource.physicalAddress, deviceType)); activeSource.physicalAddress, deviceType)); } } private void handleNewDeviceAtTheTailOfActivePath(int path) { private boolean handleNewDeviceAtTheTailOfActivePath(int path) { // Seq #22 // Seq #22 if (isTailOfActivePath(path, getActivePath())) { if (isTailOfActivePath(path, getActivePath())) { removeAction(RoutingControlAction.class); removeAction(RoutingControlAction.class); Loading @@ -564,7 +593,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange( mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange( mAddress, getActivePath(), newPath)); mAddress, getActivePath(), newPath)); addAndStartAction(new RoutingControlAction(this, newPath, false, null)); addAndStartAction(new RoutingControlAction(this, newPath, false, null)); return true; } } return false; } } /** /** Loading Loading @@ -706,8 +737,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly @ServiceThreadOnly void onNewAvrAdded(HdmiDeviceInfo avr) { void onNewAvrAdded(HdmiDeviceInfo avr) { assertRunOnServiceThread(); assertRunOnServiceThread(); if (getSystemAudioModeSetting()) { addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); if (isArcFeatureEnabled()) { } if (isArcFeatureEnabled() && !hasAction(SetArcTransmissionStateAction.class)) { startArcAction(true); startArcAction(true); } } } } Loading Loading @@ -941,6 +974,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); assertRunOnServiceThread(); if (!canStartArcUpdateAction(message.getSource(), true)) { if (!canStartArcUpdateAction(message.getSource(), true)) { if (getAvrDeviceInfo() == null) { // AVR may not have been discovered yet. Delay the message processing. mDelayedMessageBuffer.add(message); return true; } mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); if (!isConnectedToArcPort(message.getSource())) { if (!isConnectedToArcPort(message.getSource())) { displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); Loading Loading @@ -1436,6 +1474,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { super.disableDevice(initiatedByCec, callback); super.disableDevice(initiatedByCec, callback); assertRunOnServiceThread(); assertRunOnServiceThread(); mService.unregisterTvInputCallback(mTvInputCallback); // Remove any repeated working actions. // Remove any repeated working actions. // HotplugDetectionAction will be reinstated during the wake up process. // HotplugDetectionAction will be reinstated during the wake up process. // HdmiControlService.onWakeUp() -> initializeLocalDevices() -> // HdmiControlService.onWakeUp() -> initializeLocalDevices() -> Loading Loading @@ -1714,6 +1753,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); } } @ServiceThreadOnly void processAllDelayedMessages() { assertRunOnServiceThread(); mDelayedMessageBuffer.processAllMessages(); } @ServiceThreadOnly void processDelayedMessages(int address) { assertRunOnServiceThread(); mDelayedMessageBuffer.processMessagesForDevice(address); } @Override @Override protected void dump(final IndentingPrintWriter pw) { protected void dump(final IndentingPrintWriter pw) { super.dump(pw); super.dump(pw); Loading services/core/java/com/android/server/hdmi/HdmiControlService.java +27 −0 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,8 @@ import android.hardware.hdmi.IHdmiRecordListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiVendorCommandListener; import android.hardware.hdmi.IHdmiVendorCommandListener; import android.media.AudioManager; import android.media.AudioManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.net.Uri; import android.net.Uri; import android.os.Build; import android.os.Build; import android.os.Handler; import android.os.Handler; Loading Loading @@ -272,6 +274,9 @@ public final class HdmiControlService extends SystemService { @Nullable @Nullable private HdmiMhlControllerStub mMhlController; private HdmiMhlControllerStub mMhlController; @Nullable private TvInputManager mTvInputManager; // Last input port before switching to the MHL port. Should switch back to this port // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. // Gets invalidated if we go to other port/input. Loading Loading @@ -343,6 +348,28 @@ public final class HdmiControlService extends SystemService { } } } } @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mTvInputManager = (TvInputManager) getContext().getSystemService( Context.TV_INPUT_SERVICE); } } TvInputManager getTvInputManager() { return mTvInputManager; } void registerTvInputCallback(TvInputCallback callback) { if (mTvInputManager == null) return; mTvInputManager.registerCallback(callback, mHandler); } void unregisterTvInputCallback(TvInputCallback callback) { if (mTvInputManager == null) return; mTvInputManager.unregisterCallback(callback); } /** /** * Called when the initialization of local devices is complete. * Called when the initialization of local devices is complete. */ */ Loading services/core/java/com/android/server/hdmi/NewDeviceAction.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,9 @@ final class NewDeviceAction extends HdmiCecFeatureAction { mDeviceType, mVendorId, mDisplayName); mDeviceType, mVendorId, mDisplayName); tv().addCecDevice(deviceInfo); tv().addCecDevice(deviceInfo); // Consume CEC messages we already got for this newly found device. tv().processDelayedMessages(mDeviceLogicalAddress); if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { tv().onNewAvrAdded(deviceInfo); tv().onNewAvrAdded(deviceInfo); Loading Loading
services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java 0 → 100644 +133 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import java.util.ArrayList; import java.util.Iterator; /** * Buffer storage to keep incoming messages for later processing. Used to * handle messages that arrive when the device is not ready. Useful when * keeping the messages from a connected device which are not discovered yet. */ final class DelayedMessageBuffer { private final ArrayList<HdmiCecMessage> mBuffer = new ArrayList<>(); private final HdmiCecLocalDevice mDevice; DelayedMessageBuffer(HdmiCecLocalDevice device) { mDevice = device; } /** * Add a new message to the buffer. The buffer keeps selected messages in * the order they are received. * * @param message {@link HdmiCecMessage} to add */ void add(HdmiCecMessage message) { boolean buffered = true; // Note that all the messages are not handled in the same manner. // For <Active Source> we keep the latest one only. // TODO: This might not be the best way to choose the active source. // Devise a better way to pick up the best one. switch (message.getOpcode()) { case Constants.MESSAGE_ACTIVE_SOURCE: removeActiveSource(); mBuffer.add(message); break; case Constants.MESSAGE_INITIATE_ARC: mBuffer.add(message); break; default: buffered = false; break; } if (buffered) { HdmiLogger.debug("Buffering message:" + message); } } private void removeActiveSource() { // Uses iterator to remove elements while looping through the list. for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { iter.remove(); } } } void processAllMessages() { for (HdmiCecMessage message : mBuffer) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); } mBuffer.clear(); } /** * Process messages from a given logical device. Called by * {@link NewDeviceAction} actions when they finish adding the device * information. * <p><Active Source> is not processed in this method but processed * separately via {@link #processActiveSource()}. * * @param address logical address of CEC device which the messages to process * are associated with */ void processMessagesForDevice(int address) { HdmiLogger.debug("Processing message for address:" + address); for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE) { continue; } if (message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); iter.remove(); } } } /** * Process <Active Source>. * * <p>The message has a dependency on TV input framework. Should be invoked * after we get the callback * {@link android.media.tv.TvInputManager.TvInputCallback#onInputAdded(String)} * to ensure the processing of the message takes effect when transformed * to input change callback. * * @param address logical address of the device to be the active source */ void processActiveSource(int address) { for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) { HdmiCecMessage message = iter.next(); if (message.getOpcode() == Constants.MESSAGE_ACTIVE_SOURCE && message.getSource() == address) { mDevice.onMessage(message); HdmiLogger.debug("Processing message:" + message); iter.remove(); } } } }
services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -326,6 +326,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction { Slog.v(TAG, "--------------------------------------------"); Slog.v(TAG, "--------------------------------------------"); mCallback.onDeviceDiscoveryDone(result); mCallback.onDeviceDiscoveryDone(result); finish(); finish(); // Process any commands buffered while device discovery action was in progress. tv().processAllDelayedMessages(); } } private void checkAndProceedStage() { private void checkAndProceedStage() { Loading
services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +55 −4 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,8 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; import android.annotation.Nullable; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiRecordSources; import android.hardware.hdmi.HdmiRecordSources; Loading @@ -37,6 +39,9 @@ import android.hardware.hdmi.HdmiTimerRecordSources; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlCallback; import android.media.AudioManager; import android.media.AudioManager; import android.media.AudioSystem; import android.media.AudioSystem; import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.os.RemoteException; import android.os.RemoteException; import android.os.SystemProperties; import android.os.SystemProperties; import android.provider.Settings.Global; import android.provider.Settings.Global; Loading @@ -49,6 +54,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.android.server.SystemService; import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -123,6 +129,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // other CEC devices since they might not have logical address. // other CEC devices since they might not have logical address. private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>(); private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>(); // Message buffer used to buffer selected messages to process later. <Active Source> // from a source device, for instance, needs to be buffered if the device is not // discovered yet. The buffered commands are taken out and when they are ready to // handle. private final DelayedMessageBuffer mDelayedMessageBuffer = new DelayedMessageBuffer(this); // Defines the callback invoked when TV input framework is updated with input status. // We are interested in the notification for HDMI input addition event, in order to // process any CEC commands that arrived before the input is added. private final TvInputCallback mTvInputCallback = new TvInputCallback() { @Override public void onInputAdded(String inputId) { TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); if (info != null && info.isCecDevice()) { mDelayedMessageBuffer.processActiveSource(info.getLogicalAddress()); } } }; HdmiCecLocalDeviceTv(HdmiControlService service) { HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_TV); super(service, HdmiDeviceInfo.DEVICE_TV); mPrevPortId = Constants.INVALID_PORT_ID; mPrevPortId = Constants.INVALID_PORT_ID; Loading @@ -136,6 +162,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { protected void onAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); assertRunOnServiceThread(); mService.registerTvInputCallback(mTvInputCallback); mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); mAddress, mService.getPhysicalAddress(), mDeviceType)); mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( Loading Loading @@ -407,7 +434,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); if (info == null) { if (info == null) { handleNewDeviceAtTheTailOfActivePath(physicalAddress); if (!handleNewDeviceAtTheTailOfActivePath(physicalAddress)) { mDelayedMessageBuffer.add(message); } } else { } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); Loading Loading @@ -555,7 +584,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { activeSource.physicalAddress, deviceType)); activeSource.physicalAddress, deviceType)); } } private void handleNewDeviceAtTheTailOfActivePath(int path) { private boolean handleNewDeviceAtTheTailOfActivePath(int path) { // Seq #22 // Seq #22 if (isTailOfActivePath(path, getActivePath())) { if (isTailOfActivePath(path, getActivePath())) { removeAction(RoutingControlAction.class); removeAction(RoutingControlAction.class); Loading @@ -564,7 +593,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange( mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange( mAddress, getActivePath(), newPath)); mAddress, getActivePath(), newPath)); addAndStartAction(new RoutingControlAction(this, newPath, false, null)); addAndStartAction(new RoutingControlAction(this, newPath, false, null)); return true; } } return false; } } /** /** Loading Loading @@ -706,8 +737,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly @ServiceThreadOnly void onNewAvrAdded(HdmiDeviceInfo avr) { void onNewAvrAdded(HdmiDeviceInfo avr) { assertRunOnServiceThread(); assertRunOnServiceThread(); if (getSystemAudioModeSetting()) { addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); if (isArcFeatureEnabled()) { } if (isArcFeatureEnabled() && !hasAction(SetArcTransmissionStateAction.class)) { startArcAction(true); startArcAction(true); } } } } Loading Loading @@ -941,6 +974,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); assertRunOnServiceThread(); if (!canStartArcUpdateAction(message.getSource(), true)) { if (!canStartArcUpdateAction(message.getSource(), true)) { if (getAvrDeviceInfo() == null) { // AVR may not have been discovered yet. Delay the message processing. mDelayedMessageBuffer.add(message); return true; } mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); if (!isConnectedToArcPort(message.getSource())) { if (!isConnectedToArcPort(message.getSource())) { displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); displayOsd(OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT); Loading Loading @@ -1436,6 +1474,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { super.disableDevice(initiatedByCec, callback); super.disableDevice(initiatedByCec, callback); assertRunOnServiceThread(); assertRunOnServiceThread(); mService.unregisterTvInputCallback(mTvInputCallback); // Remove any repeated working actions. // Remove any repeated working actions. // HotplugDetectionAction will be reinstated during the wake up process. // HotplugDetectionAction will be reinstated during the wake up process. // HdmiControlService.onWakeUp() -> initializeLocalDevices() -> // HdmiControlService.onWakeUp() -> initializeLocalDevices() -> Loading Loading @@ -1714,6 +1753,18 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); } } @ServiceThreadOnly void processAllDelayedMessages() { assertRunOnServiceThread(); mDelayedMessageBuffer.processAllMessages(); } @ServiceThreadOnly void processDelayedMessages(int address) { assertRunOnServiceThread(); mDelayedMessageBuffer.processMessagesForDevice(address); } @Override @Override protected void dump(final IndentingPrintWriter pw) { protected void dump(final IndentingPrintWriter pw) { super.dump(pw); super.dump(pw); Loading
services/core/java/com/android/server/hdmi/HdmiControlService.java +27 −0 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,8 @@ import android.hardware.hdmi.IHdmiRecordListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiVendorCommandListener; import android.hardware.hdmi.IHdmiVendorCommandListener; import android.media.AudioManager; import android.media.AudioManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputManager.TvInputCallback; import android.net.Uri; import android.net.Uri; import android.os.Build; import android.os.Build; import android.os.Handler; import android.os.Handler; Loading Loading @@ -272,6 +274,9 @@ public final class HdmiControlService extends SystemService { @Nullable @Nullable private HdmiMhlControllerStub mMhlController; private HdmiMhlControllerStub mMhlController; @Nullable private TvInputManager mTvInputManager; // Last input port before switching to the MHL port. Should switch back to this port // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. // Gets invalidated if we go to other port/input. Loading Loading @@ -343,6 +348,28 @@ public final class HdmiControlService extends SystemService { } } } } @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mTvInputManager = (TvInputManager) getContext().getSystemService( Context.TV_INPUT_SERVICE); } } TvInputManager getTvInputManager() { return mTvInputManager; } void registerTvInputCallback(TvInputCallback callback) { if (mTvInputManager == null) return; mTvInputManager.registerCallback(callback, mHandler); } void unregisterTvInputCallback(TvInputCallback callback) { if (mTvInputManager == null) return; mTvInputManager.unregisterCallback(callback); } /** /** * Called when the initialization of local devices is complete. * Called when the initialization of local devices is complete. */ */ Loading
services/core/java/com/android/server/hdmi/NewDeviceAction.java +3 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,9 @@ final class NewDeviceAction extends HdmiCecFeatureAction { mDeviceType, mVendorId, mDisplayName); mDeviceType, mVendorId, mDisplayName); tv().addCecDevice(deviceInfo); tv().addCecDevice(deviceInfo); // Consume CEC messages we already got for this newly found device. tv().processDelayedMessages(mDeviceLogicalAddress); if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) { tv().onNewAvrAdded(deviceInfo); tv().onNewAvrAdded(deviceInfo); Loading