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

Commit 088c63e9 authored by Cody Heiner's avatar Cody Heiner
Browse files

Add MotionPredictorMetricsManager API

Adds the code for MotionPredictor to interface with the
MotionPredictorMetricsManger, with empty implementations
for the ...MetricsManger methods.

Test: `atest libinput_tests` passes.

Bug: 268245099

Change-Id: I514bea52ae914b181ed7aeb84581a30f2ebaed70
parent 233c856f
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -26,7 +26,9 @@
#include <android-base/thread_annotations.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
#include <input/MotionPredictorMetricsManager.h>
#include <input/TfLiteMotionPredictor.h>
#include <utils/Timers.h> // for nsecs_t

namespace android {

@@ -69,6 +71,7 @@ public:
     */
    MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
                    std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled);

    /**
     * Record the actual motion received by the view. This event will be used for calculating the
     * predictions.
@@ -77,7 +80,9 @@ public:
     * consistent with the previously recorded events.
     */
    android::base::Result<void> record(const MotionEvent& event);

    std::unique_ptr<MotionEvent> predict(nsecs_t timestamp);

    bool isPredictionAvailable(int32_t deviceId, int32_t source);

private:
@@ -88,6 +93,8 @@ private:

    std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
    std::optional<MotionEvent> mLastEvent;

    std::optional<MotionPredictorMetricsManager> mMetricsManager;
};

} // namespace android
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

#include <utils/Timers.h>

namespace android {

/**
 * Class to handle computing and reporting metrics for MotionPredictor.
 *
 * Currently an empty implementation, containing only the API.
 */
class MotionPredictorMetricsManager {
public:
    // Note: the MetricsManager assumes that the input interval equals the prediction interval.
    MotionPredictorMetricsManager(nsecs_t /*predictionInterval*/, size_t /*maxNumPredictions*/) {}

    void onRecord(const MotionEvent& /*inputEvent*/) {}

    void onPredict(const MotionEvent& /*predictionEvent*/) {}
};

} // namespace android
+26 −9
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
    if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
        // We still have an active gesture for another device. The provided MotionEvent is not
        // consistent the previous gesture.
        // consistent with the previous gesture.
        LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but "
                   << __func__ << " is called with " << event;
        return android::base::Error()
@@ -83,9 +83,10 @@ android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
    // Initialise the model now that it's likely to be used.
    if (!mModel) {
        mModel = TfLiteMotionPredictorModel::create();
        LOG_ALWAYS_FATAL_IF(!mModel);
    }

    if (mBuffers == nullptr) {
    if (!mBuffers) {
        mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
    }

@@ -133,6 +134,15 @@ android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
        mLastEvent = MotionEvent();
    }
    mLastEvent->copyFrom(&event, /*keepHistory=*/false);

    // Pass input event to the MetricsManager.
    if (!mMetricsManager) {
        mMetricsManager =
                std::make_optional<MotionPredictorMetricsManager>(mModel->predictionInterval(),
                                                                  mModel->outputLength());
    }
    mMetricsManager->onRecord(event);

    return {};
}

@@ -174,16 +184,17 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
    const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;

    for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) {
        const TfLiteMotionPredictorSample::Point point =
        // TODO(b/266747654): Stop predictions if confidence and/or predicted pressure are below
        // some thresholds.

        const TfLiteMotionPredictorSample::Point predictedPoint =
                convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
        // TODO(b/266747654): Stop predictions if confidence is < some threshold.

        ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y);
        ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, predictedPoint.x, predictedPoint.y);
        PointerCoords coords;
        coords.clear();
        coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x);
        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y);
        // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
        coords.setAxisValue(AMOTION_EVENT_AXIS_X, predictedPoint.x);
        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, predictedPoint.y);
        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);

        predictionTime += mModel->predictionInterval();
@@ -203,11 +214,17 @@ std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) {
        }

        axisFrom = axisTo;
        axisTo = point;
        axisTo = predictedPoint;
    }

    if (!hasPredictions) {
        return nullptr;
    }

    // Pass predictions to the MetricsManager.
    LOG_ALWAYS_FATAL_IF(!mMetricsManager);
    mMetricsManager->onPredict(*prediction);

    return prediction;
}