Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0030f4c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Revert "Revert "Implement initial statsd atoms for HDMI-CEC"""

parents 21f3ca4a d6001d41
Loading
Loading
Loading
Loading
+192 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.stats.hdmi.HdmiStatsEnums;

import com.android.internal.util.FrameworkStatsLog;

class HdmiCecAtomWriter {

    private static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
    private static final int ERROR_CODE_UNKNOWN = -1;

    /**
     * Writes a HdmiCecMessageReported atom representing an HDMI CEC message.
     * Should only be directly used for sent messages; for received messages,
     * use the overloaded version with the errorCode argument omitted.
     *
     * @param message      The HDMI CEC message
     * @param direction    Whether the message is incoming, outgoing, or neither
     * @param errorCode    The error code from the final attempt to send the message
     */
    public void messageReported(HdmiCecMessage message, int direction, int errorCode) {
        MessageReportedGenericArgs genericArgs = createMessageReportedGenericArgs(
                message, direction, errorCode);
        MessageReportedSpecialArgs specialArgs = createMessageReportedSpecialArgs(message);
        messageReportedBase(genericArgs, specialArgs);
    }

    /**
     * Version of messageReported for received messages, where no error code is present.
     *
     * @param message      The HDMI CEC message
     * @param direction    Whether the message is incoming, outgoing, or neither
     */
    public void messageReported(HdmiCecMessage message, int direction) {
        messageReported(message, direction, ERROR_CODE_UNKNOWN);
    }

    /**
     * Constructs the generic arguments for logging a HDMI CEC message.
     *
     * @param message      The HDMI CEC message
     * @param direction    Whether the message is incoming, outgoing, or neither
     * @param errorCode    The error code of the message if it's outgoing;
     *                     otherwise, ERROR_CODE_UNKNOWN
     */
    private MessageReportedGenericArgs createMessageReportedGenericArgs(
            HdmiCecMessage message, int direction, int errorCode) {
        int sendMessageResult = errorCode == ERROR_CODE_UNKNOWN
                ? HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN
                : errorCode + 10;
        return new MessageReportedGenericArgs(direction, message.getSource(),
                message.getDestination(), message.getOpcode(), sendMessageResult);
    }

    /**
     * Constructs the special arguments for logging an HDMI CEC message.
     *
     * @param message The HDMI CEC message to log
     * @return An object containing the special arguments for the message
     */
    private MessageReportedSpecialArgs createMessageReportedSpecialArgs(HdmiCecMessage message) {
        // Special arguments depend on message opcode
        switch (message.getOpcode()) {
            case Constants.MESSAGE_USER_CONTROL_PRESSED:
                return createUserControlPressedSpecialArgs(message);
            case Constants.MESSAGE_FEATURE_ABORT:
                return createFeatureAbortSpecialArgs(message);
            default:
                return new MessageReportedSpecialArgs();
        }
    }

    /**
     * Constructs the special arguments for a <User Control Pressed> message.
     *
     * @param message The HDMI CEC message to log
     */
    private MessageReportedSpecialArgs createUserControlPressedSpecialArgs(
            HdmiCecMessage message) {
        MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();

        int keycode = message.getParams()[0];
        if (keycode >= 0x1E && keycode <= 0x29) {
            specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER;
        } else {
            specialArgs.mUserControlPressedCommand = keycode + 0x100;
        }

        return specialArgs;
    }

    /**
     * Constructs method for constructing the special arguments for a <Feature Abort> message.
     *
     * @param message The HDMI CEC message to log
     */
    private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) {
        MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();

        specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte
        specialArgs.mFeatureAbortReason = message.getParams()[1] + 10;

        return specialArgs;
    }

    /**
     * Writes a HdmiCecMessageReported atom.
     *
     * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms
     * @param specialArgs Special arguments; depends on the opcode of the message
     */
    private void messageReportedBase(MessageReportedGenericArgs genericArgs,
            MessageReportedSpecialArgs specialArgs) {
        FrameworkStatsLog.write(
                FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED,
                0, // Placeholder field
                genericArgs.mDirection,
                genericArgs.mInitiatorLogicalAddress,
                genericArgs.mDestinationLogicalAddress,
                genericArgs.mOpcode,
                genericArgs.mSendMessageResult,
                specialArgs.mUserControlPressedCommand,
                specialArgs.mFeatureAbortOpcode,
                specialArgs.mFeatureAbortReason);
    }


    /**
     * Writes a HdmiCecActiveSourceChanged atom representing a change in the active source.
     *
     * @param logicalAddress             The Logical Address of the new active source
     * @param physicalAddress            The Physical Address of the new active source
     * @param relationshipToActiveSource The relationship between this device and the active source
     */
    public void activeSourceChanged(int logicalAddress, int physicalAddress,
            @Constants.PathRelationship int relationshipToActiveSource) {
        FrameworkStatsLog.write(
                FrameworkStatsLog.HDMI_CEC_ACTIVE_SOURCE_CHANGED,
                logicalAddress,
                physicalAddress,
                relationshipToActiveSource
        );
    }

    /**
     * Contains the required arguments for creating any HdmiCecMessageReported atom
     */
    private class MessageReportedGenericArgs {
        final int mDirection;
        final int mInitiatorLogicalAddress;
        final int mDestinationLogicalAddress;
        final int mOpcode;
        final int mSendMessageResult;

        MessageReportedGenericArgs(int direction, int initiatorLogicalAddress,
                int destinationLogicalAddress, int opcode, int sendMessageResult) {
            this.mDirection = direction;
            this.mInitiatorLogicalAddress = initiatorLogicalAddress;
            this.mDestinationLogicalAddress = destinationLogicalAddress;
            this.mOpcode = opcode;
            this.mSendMessageResult = sendMessageResult;
        }
    }

    /**
     * Contains the opcode-dependent arguments for creating a HdmiCecMessageReported atom. Each
     * field is initialized to a null-like value by default. Therefore, a freshly constructed
     * instance of this object represents a HDMI CEC message whose type does not require any
     * additional arguments.
     */
    private class MessageReportedSpecialArgs {
        int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN;
        int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN;
        int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN;
    }
}
+56 −15
Original line number Diff line number Diff line
@@ -28,9 +28,11 @@ import android.os.Handler;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -123,10 +125,14 @@ final class HdmiCecController {

    private final NativeWrapper mNativeWrapperImpl;

    private final HdmiCecAtomWriter mHdmiCecAtomWriter;

    // Private constructor.  Use HdmiCecController.create().
    private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
    private HdmiCecController(
            HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
        mService = service;
        mNativeWrapperImpl = nativeWrapper;
        mHdmiCecAtomWriter = atomWriter;
    }

    /**
@@ -136,19 +142,20 @@ final class HdmiCecController {
     * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
     * @param service    {@link HdmiControlService} instance used to create internal handler
     *                   and to pass callback for incoming message or event.
     * @param atomWriter {@link HdmiCecAtomWriter} instance for writing atoms for metrics.
     * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
     *         returns {@code null}.
     */
    static HdmiCecController create(HdmiControlService service) {
        return createWithNativeWrapper(service, new NativeWrapperImpl());
    static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
        return createWithNativeWrapper(service, new NativeWrapperImpl(), atomWriter);
    }

    /**
     * A factory method with injection of native methods for testing.
     */
    static HdmiCecController createWithNativeWrapper(
            HdmiControlService service, NativeWrapper nativeWrapper) {
        HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
            HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
        HdmiCecController controller = new HdmiCecController(service, nativeWrapper, atomWriter);
        String nativePtr = nativeWrapper.nativeInit();
        if (nativePtr == null) {
            HdmiLogger.warning("Couldn't get tv.cec service.");
@@ -619,7 +626,7 @@ final class HdmiCecController {
            public void run() {
                HdmiLogger.debug("[S]:" + cecMessage);
                byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
                int i = 0;
                int retransmissionCount = 0;
                int errorCode = SendMessageResult.SUCCESS;
                do {
                    errorCode = mNativeWrapperImpl.nativeSendCecCommand(
@@ -627,20 +634,25 @@ final class HdmiCecController {
                    if (errorCode == SendMessageResult.SUCCESS) {
                        break;
                    }
                } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
                } while (retransmissionCount++ < HdmiConfig.RETRANSMISSION_COUNT);

                final int finalError = errorCode;
                if (finalError != SendMessageResult.SUCCESS) {
                    Slog.w(TAG, "Failed to send " + cecMessage + " with errorCode=" + finalError);
                }
                if (callback != null) {
                runOnServiceThread(new Runnable() {
                    @Override
                    public void run() {
                        mHdmiCecAtomWriter.messageReported(
                                cecMessage,
                                FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED__DIRECTION__OUTGOING,
                                finalError
                        );
                        if (callback != null) {
                            callback.onSendCompleted(finalError);
                        }
                    });
                    }
                });
            }
        });
    }
@@ -654,9 +666,38 @@ final class HdmiCecController {
        HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
        HdmiLogger.debug("[R]:" + command);
        addCecMessageToHistory(true /* isReceived */, command);

        mHdmiCecAtomWriter.messageReported(command,
                incomingMessageDirection(srcAddress, dstAddress));

        onReceiveCommand(command);
    }

    /**
     * Computes the direction of an incoming message, as implied by the source and
     * destination addresses. This will usually return INCOMING; if not, it can indicate a bug.
     */
    private int incomingMessageDirection(int srcAddress, int dstAddress) {
        boolean sourceIsLocal = false;
        boolean destinationIsLocal = false;
        for (HdmiCecLocalDevice localDevice : getLocalDeviceList()) {
            int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
            if (logicalAddress == srcAddress) {
                sourceIsLocal = true;
            }
            if (logicalAddress == dstAddress) {
                destinationIsLocal = true;
            }
        }

        if (!sourceIsLocal && destinationIsLocal) {
            return HdmiStatsEnums.INCOMING;
        } else if (sourceIsLocal && destinationIsLocal) {
            return HdmiStatsEnums.TO_SELF;
        }
        return HdmiStatsEnums.MESSAGE_DIRECTION_OTHER;
    }

    /**
     * Called when a hotplug event issues.
     */
+14 −1
Original line number Diff line number Diff line
@@ -397,6 +397,10 @@ public class HdmiControlService extends SystemService {
    // Set to true if the logical address allocation is completed.
    private boolean mAddressAllocated = false;

    // Object that handles logging statsd atoms.
    // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing.
    private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter();

    // Buffer for processing the incoming cec messages while allocating logical addresses.
    private final class CecMessageBuffer {
        private List<HdmiCecMessage> mBuffer = new ArrayList<>();
@@ -509,7 +513,7 @@ public class HdmiControlService extends SystemService {
        mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);

        if (mCecController == null) {
            mCecController = HdmiCecController.create(this);
            mCecController = HdmiCecController.create(this, getAtomWriter());
        }
        if (mCecController != null) {
            if (mHdmiControlEnabled) {
@@ -3233,6 +3237,10 @@ public class HdmiControlService extends SystemService {
            mActiveSource.logicalAddress = logicalAddress;
            mActiveSource.physicalAddress = physicalAddress;
        }

        getAtomWriter().activeSourceChanged(logicalAddress, physicalAddress,
                HdmiUtils.pathRelationship(getPhysicalAddress(), physicalAddress));

        // If the current device is a source device, check if the current Active Source matches
        // the local device info. Set mIsActiveSource of the local device accordingly.
        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
@@ -3363,6 +3371,11 @@ public class HdmiControlService extends SystemService {
        }
    }

    @VisibleForTesting
    HdmiCecAtomWriter getAtomWriter() {
        return mAtomWriter;
    }

    boolean isMhlInputChangeEnabled() {
        synchronized (mLock) {
            return mMhlInputChangeEnabled;
+1 −1
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ public class ActiveSourceActionTest {
        mHdmiControlService.setIoLooper(looper);
        mNativeWrapper = new FakeNativeWrapper();
        HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
                this.mHdmiControlService, mNativeWrapper);
                this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
        mHdmiControlService.setCecController(hdmiCecController);
        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+2 −2
Original line number Diff line number Diff line
@@ -120,8 +120,8 @@ public class ArcInitiationActionFromAvrTest {
        Looper looper = mTestLooper.getLooper();
        mHdmiControlService.setIoLooper(looper);
        mNativeWrapper = new FakeNativeWrapper();
        mHdmiCecController =
                HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper);
        mHdmiCecController = HdmiCecController.createWithNativeWrapper(
                this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
        mHdmiControlService.setCecController(mHdmiCecController);
        mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
        mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
Loading