diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 461a9b0636e46..4a10f50dde5a6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -29,9 +29,12 @@ import com.android.internal.util.Predicate; import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; import libcore.util.EmptyArray; import sun.util.locale.LanguageTag; @@ -69,6 +72,8 @@ final class HdmiCecController { private static final int NUM_LOGICAL_ADDRESS = 16; + private static final int MAX_CEC_MESSAGE_HISTORY = 20; + // Predicate for whether the given logical address is remote device's one or not. private final Predicate mRemoteDeviceAddressPredicate = new Predicate() { @Override @@ -101,6 +106,10 @@ final class HdmiCecController { // Stores the local CEC devices in the system. Device type is used for key. private final SparseArray mLocalDevices = new SparseArray<>(); + // Stores recent CEC messages history for debugging purpose. + private final ArrayBlockingQueue mMessageHistory = + new ArrayBlockingQueue<>(MAX_CEC_MESSAGE_HISTORY); + // Private constructor. Use HdmiCecController.create(). private HdmiCecController(HdmiControlService service) { mService = service; @@ -580,6 +589,7 @@ final class HdmiCecController { void sendCommand(final HdmiCecMessage cecMessage, final HdmiControlService.SendMessageCallback callback) { assertRunOnServiceThread(); + addMessageToHistory(false /* isReceived */, cecMessage); runOnIoThread(new Runnable() { @Override public void run() { @@ -597,7 +607,7 @@ final class HdmiCecController { final int finalError = errorCode; if (finalError != SendMessageResult.SUCCESS) { - Slog.w(TAG, "Failed to send " + cecMessage); + Slog.w(TAG, "Failed to send " + cecMessage + " with errorCode=" + finalError); } if (callback != null) { runOnServiceThread(new Runnable() { @@ -619,6 +629,7 @@ final class HdmiCecController { assertRunOnServiceThread(); HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body); HdmiLogger.debug("[R]:" + command); + addMessageToHistory(true /* isReceived */, command); onReceiveCommand(command); } @@ -632,6 +643,16 @@ final class HdmiCecController { mService.onHotplug(port, connected); } + @ServiceThreadOnly + private void addMessageToHistory(boolean isReceived, HdmiCecMessage message) { + assertRunOnServiceThread(); + MessageHistoryRecord record = new MessageHistoryRecord(isReceived, message); + if (!mMessageHistory.offer(record)) { + mMessageHistory.poll(); + mMessageHistory.offer(record); + } + } + void dump(final IndentingPrintWriter pw) { for (int i = 0; i < mLocalDevices.size(); ++i) { pw.println("HdmiCecLocalDevice #" + i + ":"); @@ -639,6 +660,13 @@ final class HdmiCecController { mLocalDevices.valueAt(i).dump(pw); pw.decreaseIndent(); } + pw.println("CEC message history:"); + pw.increaseIndent(); + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (MessageHistoryRecord record : mMessageHistory) { + record.dump(pw, sdf); + } + pw.decreaseIndent(); } private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue); @@ -654,4 +682,24 @@ final class HdmiCecController { private static native void nativeSetLanguage(long controllerPtr, String language); private static native void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag); private static native boolean nativeIsConnected(long controllerPtr, int port); + + private final class MessageHistoryRecord { + private final long mTime; + private final boolean mIsReceived; // true if received message and false if sent message + private final HdmiCecMessage mMessage; + + public MessageHistoryRecord(boolean isReceived, HdmiCecMessage message) { + mTime = System.currentTimeMillis(); + mIsReceived = isReceived; + mMessage = message; + } + + void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) { + pw.print(mIsReceived ? "[R]" : "[S]"); + pw.print(" time="); + pw.print(sdf.format(new Date(mTime))); + pw.print(" message="); + pw.println(mMessage); + } + } }