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

Commit 8f71d573 authored by Jared Duke's avatar Jared Duke
Browse files

Move InputEventReceiver tracing to native code

InputEventReceiver.dispatchInputEvent is called with relatively
high frequency. Even when guarding the associated trace call and
string creation, it was showing up as a significant source of
StringBuilder and JNI overhead for some common traced scenarios.

Move the trace calls into native where `dispatchInputEvent` is
invoked. MotionEvent itself is backed by native data structures, so
associated member field queries are even cheaper when we do this
tracing natively.

Bug: 444721071
Test: presubmit
Test: ./record_android_trace -c syshealth.pbtx + compare traces
Flag: EXEMPT PURE_REFACTOR
Change-Id: I7bf72a8a83418ab4e987a87e288566e10f782058
parent 17cab722
Loading
Loading
Loading
Loading
+0 −24
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.Trace;
import android.util.Log;
import android.util.SparseIntArray;

@@ -270,35 +269,12 @@ public abstract class InputEventReceiver {
        return nativeGetToken(mReceiverPtr);
    }

    private String getShortDescription(InputEvent event) {
        if (event instanceof MotionEvent motion) {
            return "MotionEvent " + MotionEvent.actionToString(motion.getAction()) + " deviceId="
                    + motion.getDeviceId() + " source=0x"
                    + Integer.toHexString(motion.getSource()) +  " historySize="
                    + motion.getHistorySize();
        } else if (event instanceof KeyEvent key) {
            return "KeyEvent " + KeyEvent.actionToString(key.getAction())
                    + " deviceId=" + key.getDeviceId();
        } else {
            Log.wtf(TAG, "Illegal InputEvent type: " + event);
            return "InputEvent";
        }
    }

    // Called from native code.
    @SuppressWarnings("unused")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private void dispatchInputEvent(int seq, InputEvent event) {
        if (Trace.isTagEnabled(Trace.TRACE_TAG_INPUT)) {
            // This 'if' block is an optimization - without it, 'getShortDescription' will be
            // called unconditionally, which is expensive.
            Trace.traceBegin(Trace.TRACE_TAG_INPUT,
                    "dispatchInputEvent " + getShortDescription(event));
        }
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
        // If tracing is not enabled, `traceEnd` is a no-op (so we don't need to guard it with 'if')
        Trace.traceEnd(Trace.TRACE_TAG_INPUT);
    }

    /**
+34 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include <utils/Looper.h>

#include <cinttypes>
#include <sstream>
#include <variant>
#include <vector>

@@ -47,6 +48,8 @@
#include "android_view_MotionEvent.h"
#include "core_jni_helpers.h"

using android::base::StringPrintf;

namespace android {

namespace {
@@ -88,6 +91,32 @@ std::string addPrefix(std::string str, std::string_view prefix) {
    }
    return str;
}

std::string getDispatchInputEventTraceDescription(const InputEvent& inputEvent) {
    // For KEY and MOTION events, only print an abbreviated description. For other event types, fall
    // back to the standard description (useful for debugging, but shouldn't happen in practice).
    switch (inputEvent.getType()) {
        case InputEventType::KEY: {
            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent);
            return StringPrintf("dispatchInputEvent KeyEvent %s deviceId=%d",
                                KeyEvent::actionToString(keyEvent.getAction()),
                                keyEvent.getDeviceId());
        }
        case InputEventType::MOTION: {
            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent);
            return StringPrintf("dispatchInputEvent MotionEvent %s deviceId=%d "
                                "source=0x%" PRIx32 ", historySize=%zu",
                                MotionEvent::actionToString(motionEvent.getAction()).c_str(),
                                motionEvent.getDeviceId(), motionEvent.getSource(),
                                motionEvent.getHistorySize());
        }
        default: {
            std::ostringstream description;
            description << "dispatchInputEvent " << inputEvent;
            return description.str();
        }
    }
}
} // namespace

class NativeInputEventReceiver;
@@ -579,9 +608,14 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Dispatching input event.", mName.c_str());
                }
                if (ATRACE_ENABLED()) {
                    std::string description = getDispatchInputEventTraceDescription(*inputEvent);
                    ATRACE_BEGIN(description.c_str());
                }
                env->CallVoidMethod(receiverObj.get(),
                                    gInputEventReceiverClassInfo.dispatchInputEvent, seq,
                                    inputEventObj.get());
                ATRACE_END();
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;