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

Commit a51cbe30 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Prediction for motion events

The class MotionPredictor will be used to call into native code to
execute prediction algorithm.

The feature can be enabled by setting the appropriate config on the
device.

To enable the feature:
adb shell setprop persist.input.enable_motion_prediction true

Bug: 210158587
Test: m MotionPrediction && adb install $ANDROID_PRODUCT_OUT/system/app/MotionPrediction/MotionPrediction.apk
Change-Id: Iaee80d99b18da2f788fa8eacf2e14a6b5c6a7088
parent 251c7563
Loading
Loading
Loading
Loading
+116 −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.
 */

package android.view;

import android.annotation.NonNull;
import android.content.Context;

import libcore.util.NativeAllocationRegistry;

import java.util.Arrays;
import java.util.List;

/**
 * Calculates motion predictions.
 *
 * Add motions here to get predicted events!
 * @hide
 */
// Acts as a pass-through to the native MotionPredictor object.
// Do not store any state in this Java layer, or add any business logic here. All of the
// implementation details should go into the native MotionPredictor.
// The context / resource access must be here rather than in native layer due to the lack of the
// corresponding native API surface.
public final class MotionPredictor {

    private static class RegistryHolder {
        public static final NativeAllocationRegistry REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        MotionPredictor.class.getClassLoader(),
                        nativeGetNativeMotionPredictorFinalizer());
    }

    // Pointer to the native object.
    private final long mPtr;
    private final Context mContext;

    /**
     * Create a new MotionPredictor for the provided {@link Context}.
     * @param context The context for the predictions
     */
    public MotionPredictor(@NonNull Context context) {
        mContext = context;
        final int offsetNanos = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_motionPredictionOffsetNanos);
        mPtr = nativeInitialize(offsetNanos);
        RegistryHolder.REGISTRY.registerNativeAllocation(this, mPtr);
    }

    /**
     * Record a movement so that in the future, a prediction for the current gesture can be
     * generated. Ensure to add all motions from the gesture of interest to generate the correct
     * prediction.
     * @param event The received event
     */
    public void record(@NonNull MotionEvent event) {
        nativeRecord(mPtr, event);
    }

    /**
     * Get predicted events for all gestures that have been provided to the 'record' function.
     * If events from multiple devices were sent to 'record', this will produce a separate
     * {@link MotionEvent} for each device id. The returned list may be empty if no predictions for
     * any of the added events are available.
     * Predictions may not reach the requested timestamp if the confidence in the prediction results
     * is low.
     *
     * @param predictionTimeNanos The time that the prediction should target, in the
     * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds.
     *
     * @return the list of predicted motion events, for each device id. Ensure to check the
     * historical data in addition to the latest ({@link MotionEvent#getX getX()},
     * {@link MotionEvent#getY getY()}) coordinates for smoothest prediction curves. Empty list is
     * returned if predictions are not supported, or not possible for the current set of gestures.
     */
    @NonNull
    public List<MotionEvent> predict(long predictionTimeNanos) {
        return Arrays.asList(nativePredict(mPtr, predictionTimeNanos));
    }

    /**
     * Check whether this device supports motion predictions for the given source type.
     *
     * @param deviceId The input device id
     * @param source The source of input events
     * @return True if the current device supports predictions, false otherwise.
     */
    public boolean isPredictionAvailable(int deviceId, int source) {
        // Device-specific override
        if (!mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableMotionPrediction)) {
            return false;
        }
        return nativeIsPredictionAvailable(mPtr, deviceId, source);
    }

    private static native long nativeInitialize(int offsetNanos);
    private static native void nativeRecord(long nativePtr, MotionEvent event);
    private static native MotionEvent[] nativePredict(long nativePtr, long predictionTimeNanos);
    private static native boolean nativeIsPredictionAvailable(long nativePtr, int deviceId,
            int source);
    private static native long nativeGetNativeMotionPredictorFinalizer();
}
+2 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ cc_library_shared {
                "android_view_KeyCharacterMap.cpp",
                "android_view_KeyEvent.cpp",
                "android_view_MotionEvent.cpp",
                "android_view_MotionPredictor.cpp",
                "android_view_PointerIcon.cpp",
                "android_view_Surface.cpp",
                "android_view_SurfaceControl.cpp",
@@ -283,6 +284,7 @@ cc_library_shared {
                "libhwui",
                "libmediandk",
                "libpermission",
                "libPlatformProperties",
                "libsensor",
                "libinput",
                "libcamera_client",
+2 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyCharacterMap(JNIEnv *env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_MotionPredictor(JNIEnv* env);
extern int register_android_view_PointerIcon(JNIEnv* env);
extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_view_VerifiedKeyEvent(JNIEnv* env);
@@ -1643,6 +1644,7 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_android_view_InputQueue),
        REG_JNI(register_android_view_KeyEvent),
        REG_JNI(register_android_view_MotionEvent),
        REG_JNI(register_android_view_MotionPredictor),
        REG_JNI(register_android_view_PointerIcon),
        REG_JNI(register_android_view_VelocityTracker),
        REG_JNI(register_android_view_VerifiedKeyEvent),
+2 −4
Original line number Diff line number Diff line
@@ -97,7 +97,6 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
    return env->NewLocalRef(inputDeviceObj.get());
}


int register_android_view_InputDevice(JNIEnv* env)
{
    gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
@@ -108,9 +107,8 @@ int register_android_view_InputDevice(JNIEnv* env)
                                                  "String;ZIILandroid/view/KeyCharacterMap;Ljava/"
                                                  "lang/String;Ljava/lang/String;ZZZZZZ)V");

    gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
            "addMotionRange", "(IIFFFFF)V");

    gInputDeviceClassInfo.addMotionRange =
            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
    return 0;
}

+14 −0
Original line number Diff line number Diff line
@@ -102,6 +102,20 @@ jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& ev
    return eventObj;
}

jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env, std::unique_ptr<MotionEvent> event) {
    if (event == nullptr) {
        return nullptr;
    }
    jobject eventObj =
            env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain);
    if (env->ExceptionCheck() || !eventObj) {
        LOGE_EX(env);
        LOG_ALWAYS_FATAL("An exception occurred while obtaining a Java motion event.");
    }
    android_view_MotionEvent_setNativePtr(env, eventObj, event.release());
    return eventObj;
}

status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
    env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
    if (env->ExceptionCheck()) {
Loading