diff --git a/core/api/current.txt b/core/api/current.txt
index b17e3343666e626c8fb46ada1397415a5f410ff6..2465ffeef67fb515a9d9d69c490e68558dcc588f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20266,6 +20266,7 @@ package android.hardware.input {
     method @Nullable public android.hardware.input.HostUsiVersion getHostUsiVersion(@NonNull android.view.Display);
     method @Nullable public android.view.InputDevice getInputDevice(int);
     method public int[] getInputDeviceIds();
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") @Nullable public android.view.InputDevice.ViewBehavior getInputDeviceViewBehavior(int);
     method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
     method public boolean isStylusPointerIconEnabled();
     method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -50437,6 +50438,10 @@ package android.view {
     method public boolean isFromSource(int);
   }
 
+  @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public static final class InputDevice.ViewBehavior {
+    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public boolean shouldSmoothScroll(int, int);
+  }
+
   public abstract class InputEvent implements android.os.Parcelable {
     method public int describeContents();
     method public final android.view.InputDevice getDevice();
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 4ebbde732747b054ea18c31ffcfb1f5ba493c790..db992cdd20dbc34cd7460b104d71ffaff59a6ef0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,11 @@
 
 package android.hardware.input;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
 import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;
 
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -293,6 +295,23 @@ public final class InputManager {
         return mGlobal.getInputDevice(id);
     }
 
+    /**
+     * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}.
+     *
+     * <p>Use this API to query a fresh view behavior instance whenever the input device
+     * changes.
+     *
+     * @param deviceId the id of the input device whose view behavior is being requested.
+     * @return the view behavior of the input device with the provided id, or {@code null} if there
+     *      is not input device with the provided id.
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    @Nullable
+    public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) {
+        InputDevice device = getInputDevice(deviceId);
+        return device == null ? null : device.getViewBehavior();
+    }
+
     /**
      * Gets information about the input device with the specified descriptor.
      * @param descriptor The input device descriptor.
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index f2c3abc8edb4755437b35e6a1b1b2dd2f34e8e56..891e2a2d4b2048631380fdc0830862d18befe307 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,7 +16,10 @@
 
 package android.view;
 
+import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
+
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,6 +31,7 @@ import android.hardware.BatteryState;
 import android.hardware.SensorManager;
 import android.hardware.input.HostUsiVersion;
 import android.hardware.input.InputDeviceIdentifier;
+import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
 import android.hardware.lights.LightsManager;
 import android.icu.util.ULocale;
@@ -90,6 +94,8 @@ public final class InputDevice implements Parcelable {
     private final int mAssociatedDisplayId;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
+    private final ViewBehavior mViewBehavior = new ViewBehavior(this);
+
     @GuardedBy("mMotionRanges")
     private Vibrator mVibrator; // guarded by mMotionRanges during initialization
 
@@ -539,6 +545,8 @@ public final class InputDevice implements Parcelable {
             addMotionRange(in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
                     in.readFloat(), in.readFloat(), in.readFloat());
         }
+
+        mViewBehavior.mShouldSmoothScroll = in.readBoolean();
     }
 
     /**
@@ -571,6 +579,7 @@ public final class InputDevice implements Parcelable {
         private int mUsiVersionMinor = -1;
         private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
         private List<MotionRange> mMotionRanges = new ArrayList<>();
+        private boolean mShouldSmoothScroll;
 
         /** @see InputDevice#getId() */
         public Builder setId(int id) {
@@ -706,6 +715,16 @@ public final class InputDevice implements Parcelable {
             return this;
         }
 
+        /**
+         * Sets the view behavior for smooth scrolling ({@code false} by default).
+         *
+         * @see ViewBehavior#shouldSmoothScroll(int, int)
+         */
+        public Builder setShouldSmoothScroll(boolean shouldSmoothScroll) {
+            mShouldSmoothScroll = shouldSmoothScroll;
+            return this;
+        }
+
         /** Build {@link InputDevice}. */
         public InputDevice build() {
             InputDevice device = new InputDevice(
@@ -745,6 +764,8 @@ public final class InputDevice implements Parcelable {
                         range.getResolution());
             }
 
+            device.setShouldSmoothScroll(mShouldSmoothScroll);
+
             return device;
         }
     }
@@ -1123,6 +1144,22 @@ public final class InputDevice implements Parcelable {
         return mMotionRanges;
     }
 
+    /**
+     * Provides the {@link ViewBehavior} for the device.
+     *
+     * <p>This behavior is designed to be obtained using the
+     * {@link InputManager#getInputDeviceViewBehavior(int)} API, to allow associating the behavior
+     * with a {@link Context} (since input device is not associated with a context).
+     * The ability to associate the behavior with a context opens capabilities like linking the
+     * behavior to user settings, for example.
+     *
+     * @hide
+     */
+    @NonNull
+    public ViewBehavior getViewBehavior() {
+        return mViewBehavior;
+    }
+
     // Called from native code.
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void addMotionRange(int axis, int source,
@@ -1130,6 +1167,11 @@ public final class InputDevice implements Parcelable {
         mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
     }
 
+    // Called from native code.
+    private void setShouldSmoothScroll(boolean shouldSmoothScroll) {
+        mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;
+    }
+
     /**
      * Returns the Bluetooth address of this input device, if known.
      *
@@ -1447,6 +1489,82 @@ public final class InputDevice implements Parcelable {
         }
     }
 
+    /**
+     * Provides information on how views processing {@link MotionEvent}s generated by this input
+     * device should respond to the events. Use {@link InputManager#getInputDeviceViewBehavior(int)}
+     * to get an instance of the view behavior for an input device.
+     *
+     * <p>See an example below how a {@link View} can use this class to determine and apply the
+     * scrolling behavior for a generic {@link MotionEvent}.
+     *
+     * <pre>{@code
+     *     public boolean onGenericMotionEvent(MotionEvent event) {
+     *         InputManager manager = context.getSystemService(InputManager.class);
+     *         ViewBehavior viewBehavior = manager.getInputDeviceViewBehavior(event.getDeviceId());
+     *         // Assume a helper function that tells us which axis to use for scrolling purpose.
+     *         int axis = getScrollAxisForGenericMotionEvent(event);
+     *         int source = event.getSource();
+     *
+     *         boolean shouldSmoothScroll =
+     *                 viewBehavior != null && viewBehavior.shouldSmoothScroll(axis, source);
+     *         // Proceed to running the scrolling logic...
+     *     }
+     * }</pre>
+     *
+     * @see InputManager#getInputDeviceViewBehavior(int)
+     */
+    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+    public static final class ViewBehavior {
+        private static final boolean DEFAULT_SHOULD_SMOOTH_SCROLL = false;
+
+        private final InputDevice mInputDevice;
+
+        // TODO(b/246946631): implement support for InputDevices to adjust this configuration
+        // by axis and source. When implemented, the axis/source specific config will take
+        // precedence over this global config.
+        /** A global smooth scroll configuration applying to all motion axis and input source. */
+        private boolean mShouldSmoothScroll = DEFAULT_SHOULD_SMOOTH_SCROLL;
+
+        /** @hide */
+        public ViewBehavior(@NonNull InputDevice inputDevice) {
+            mInputDevice = inputDevice;
+        }
+
+        /**
+         * Returns whether a view should smooth scroll when scrolling due to a {@link MotionEvent}
+         * generated by the input device.
+         *
+         * <p>Smooth scroll in this case refers to a scroll that animates the transition between
+         * the starting and ending positions of the scroll. When this method returns {@code true},
+         * views should try to animate a scroll generated by this device at the given axis and with
+         * the given source to produce a good scroll user experience. If this method returns
+         * {@code false}, animating scrolls is not necessary.
+         *
+         * <p>If the input device does not have a {@link MotionRange} with the provided axis and
+         * source, this method returns {@code false}.
+         *
+         * @param axis the {@link MotionEvent} axis whose value is used to get the scroll extent.
+         * @param source the {link InputDevice} source from which the {@link MotionEvent} that
+         *      triggers the scroll came.
+         * @return {@code true} if smooth scrolling should be used for the scroll, or {@code false}
+         *      if smooth scrolling is not necessary, or if the provided axis and source combination
+         *      is not available for the input device.
+         */
+        @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
+        public boolean shouldSmoothScroll(int axis, int source) {
+            // Note: although we currently do not use axis and source in computing the return value,
+            // we will keep the API params to avoid further public API changes when we start
+            // supporting axis/source configuration. Also, having these params lets OEMs provide
+            // their custom implementation of the API that depends on axis and source.
+
+            // TODO(b/246946631): speed up computation using caching of results.
+            if (mInputDevice.getMotionRange(axis, source) == null) {
+                return false;
+            }
+            return mShouldSmoothScroll;
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel out, int flags) {
         mKeyCharacterMap.writeToParcel(out, flags);
@@ -1484,6 +1602,8 @@ public final class InputDevice implements Parcelable {
             out.writeFloat(range.mFuzz);
             out.writeFloat(range.mResolution);
         }
+
+        out.writeBoolean(mViewBehavior.mShouldSmoothScroll);
     }
 
     @Override
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 239c6260800bc9e0591b4c41228cd27c61bc1ad3..aae0da9006a24c2ec4099ba2fd8af448802ffa07 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-#include <input/Input.h>
+#include "android_view_InputDevice.h"
 
 #include <android_runtime/AndroidRuntime.h>
+#include <com_android_input_flags.h>
+#include <input/Input.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
-
 #include <nativehelper/ScopedLocalRef.h>
 
-#include "android_view_InputDevice.h"
 #include "android_view_KeyCharacterMap.h"
-
 #include "core_jni_helpers.h"
 
 namespace android {
@@ -34,6 +33,7 @@ static struct {
 
     jmethodID ctor;
     jmethodID addMotionRange;
+    jmethodID setShouldSmoothScroll;
 } gInputDeviceClassInfo;
 
 jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
@@ -103,6 +103,18 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
         }
     }
 
+    if (com::android::input::flags::input_device_view_behavior_api()) {
+        const InputDeviceViewBehavior& viewBehavior = deviceInfo.getViewBehavior();
+        std::optional<bool> defaultSmoothScroll = viewBehavior.shouldSmoothScroll;
+        if (defaultSmoothScroll.has_value()) {
+            env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.setShouldSmoothScroll,
+                                *defaultSmoothScroll);
+            if (env->ExceptionCheck()) {
+                return NULL;
+            }
+        }
+    }
+
     return env->NewLocalRef(inputDeviceObj.get());
 }
 
@@ -118,6 +130,8 @@ int register_android_view_InputDevice(JNIEnv* env)
 
     gInputDeviceClassInfo.addMotionRange =
             GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
+    gInputDeviceClassInfo.setShouldSmoothScroll =
+            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "setShouldSmoothScroll", "(Z)V");
     return 0;
 }
 
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 5434c82b07bde7e1a60bb2ee3dc86e943f1ed1f7..5f1bc8748db8f260ea1bb8f30eeffaee93fececa 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -67,8 +67,14 @@ public class InputDeviceTest {
         assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);
 
         for (int j = 0; j < device.getMotionRanges().size(); j++) {
-            assertMotionRangeEquals(device.getMotionRanges().get(j),
-                    outDevice.getMotionRanges().get(j));
+            InputDevice.MotionRange motionRange = device.getMotionRanges().get(j);
+            assertMotionRangeEquals(motionRange, outDevice.getMotionRanges().get(j));
+
+            int axis = motionRange.getAxis();
+            int source = motionRange.getSource();
+            assertEquals(
+                    device.getViewBehavior().shouldSmoothScroll(axis, source),
+                    outDevice.getViewBehavior().shouldSmoothScroll(axis, source));
         }
     }
 
@@ -93,7 +99,8 @@ public class InputDeviceTest {
                 .setHasBattery(true)
                 .setKeyboardLanguageTag("en-US")
                 .setKeyboardLayoutType("qwerty")
-                .setUsiVersion(new HostUsiVersion(2, 0));
+                .setUsiVersion(new HostUsiVersion(2, 0))
+                .setShouldSmoothScroll(true);
 
         for (int i = 0; i < 30; i++) {
             deviceBuilder.addMotionRange(