// Copyright 2024 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_ANDROID_INPUT_HINT_CHECKER_H_ #define BASE_ANDROID_INPUT_HINT_CHECKER_H_ #include #include "base/android/jni_weak_ref.h" #include "base/base_export.h" #include "base/feature_list.h" #include "base/memory/raw_ptr.h" #include "base/no_destructor.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" namespace base::android { BASE_DECLARE_FEATURE(kYieldWithInputHint); // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. // Distinguishes outcomes of returning |true| from HasInput() below. enum class InputHintResult { // The yield went through the Looper and dispatched input in // CompositorViewHolder. This path probably reduces touch latency in the // web contents area. kCompositorViewTouchEvent = 0, // The yield returned back from the Looper to continue with native tasks. It // can happen because the Looper did not prioritize input handling or // because the input events were hitting the parts of the UI outside of the // renderer compositor view. kBackToNative = 1, kMaxValue = kBackToNative, }; // A class to track a single global root View object and ask it for presence of // new unhandled input events. // // This class uses bits specific to Android V and does nothing on earlier // releases. // // Must be constructed on UI thread. All public methods must be called on the UI // thread. class BASE_EXPORT InputHintChecker { public: InputHintChecker(); virtual ~InputHintChecker(); // Returns the singleton. static InputHintChecker& GetInstance(); // Initializes features for this class. See `base::features::Init()`. static void InitializeFeatures(); // Obtains a weak reference to |root_view| so that the following calls to // HasInput() take the input hint for this View. Requirements for the View // object are described in InputHintChecker.java. void SetView(JNIEnv* env, const jni_zero::JavaParamRef& root_view); // Fetches and returns the input hint from the Android Framework. // // Works as a hint: when unhandled input events are detected, this method // returns |true| with high probability. However, the returned value neither // guarantees presence nor absence of input events in the queue. For example, // this method returns |false| while the singleton is going through // initialization. // // Throttles the calls to one every few milliseconds. When a call is made // before the minimal time interval passed since the previous call, returns // false. static bool HasInput() { return false; } // RAII override of GetInstance() for testing. struct ScopedOverrideInstance { explicit ScopedOverrideInstance(InputHintChecker* checker); ~ScopedOverrideInstance(); }; // Used for UMA metrics to remember that the input hint was used to yield // recently. void set_is_after_input_yield(bool after) { is_after_input_yield_ = after; } bool is_after_input_yield() { return is_after_input_yield_; } // Used to test UMA metric recording. void disable_metric_subsampling() { metric_subsampling_disabled_ = true; } // Records the UMA metric based on the InputHintResult. void RecordInputHintResult(InputHintResult result); bool IsInitializedForTesting(); bool FailedToInitializeForTesting(); bool HasInputImplNoThrottlingForTesting(_JNIEnv* env); bool HasInputImplWithThrottlingForTesting(_JNIEnv* env); protected: virtual bool HasInputImplWithThrottling(); private: friend class base::NoDestructor; class OffThreadInitInvoker; enum class InitState; InitState FetchState() const; void TransitionToState(InitState new_state); void RunOffThreadInitialization(); void InitGlobalRefsAndMethodIds(JNIEnv* env); bool HasInputImpl(JNIEnv* env, jobject o); bool is_after_input_yield_ = false; bool metric_subsampling_disabled_ = false; // Last time the input hint was requested. Used for throttling. base::TimeTicks last_checked_; // Initialization state. It is made atomic because part of the initialization // happens on another thread while public methods of this class can be called // on the UI thread. std::atomic init_state_; // The android.view.View object reference used to fetch the hint in // HasInput(). JavaObjectWeakGlobalRef view_; // Represents a reference to android.view.View.class. Used during // initialization. ScopedJavaGlobalRef view_class_; // Represents a reference to object of type j.l.reflect.Method for // View#probablyHasInput(). ScopedJavaGlobalRef reflect_method_for_has_input_; // The ID corresponding to j.l.reflect.Method#invoke(Object, Object...). jmethodID invoke_id_; // The ID corresponding to j.l.Boolean#booleanValue(). jmethodID boolean_value_id_; THREAD_CHECKER(thread_checker_); }; } // namespace base::android #endif // BASE_ANDROID_INPUT_HINT_CHECKER_H_