1 // Copyright 2024 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_ANDROID_INPUT_HINT_CHECKER_H_ 6 #define BASE_ANDROID_INPUT_HINT_CHECKER_H_ 7 8 #include <jni.h> 9 10 #include "base/android/jni_weak_ref.h" 11 #include "base/base_export.h" 12 #include "base/feature_list.h" 13 #include "base/memory/raw_ptr.h" 14 #include "base/no_destructor.h" 15 #include "base/threading/thread_checker.h" 16 #include "base/time/time.h" 17 18 namespace base::android { 19 20 BASE_DECLARE_FEATURE(kYieldWithInputHint); 21 22 // These values are persisted to logs. Entries should not be renumbered and 23 // numeric values should never be reused. 24 // Distinguishes outcomes of returning |true| from HasInput() below. 25 enum class InputHintResult { 26 // The yield went through the Looper and dispatched input in 27 // CompositorViewHolder. This path probably reduces touch latency in the 28 // web contents area. 29 kCompositorViewTouchEvent = 0, 30 // The yield returned back from the Looper to continue with native tasks. It 31 // can happen because the Looper did not prioritize input handling or 32 // because the input events were hitting the parts of the UI outside of the 33 // renderer compositor view. 34 kBackToNative = 1, 35 kMaxValue = kBackToNative, 36 }; 37 38 // A class to track a single global root View object and ask it for presence of 39 // new unhandled input events. 40 // 41 // This class uses bits specific to Android V and does nothing on earlier 42 // releases. 43 // 44 // Must be constructed on UI thread. All public methods must be called on the UI 45 // thread. 46 class BASE_EXPORT InputHintChecker { 47 public: 48 InputHintChecker(); 49 virtual ~InputHintChecker(); 50 51 // Returns the singleton. 52 static InputHintChecker& GetInstance(); 53 54 // Initializes features for this class. See `base::features::Init()`. 55 static void InitializeFeatures(); 56 57 // Obtains a weak reference to |root_view| so that the following calls to 58 // HasInput() take the input hint for this View. Requirements for the View 59 // object are described in InputHintChecker.java. 60 void SetView(JNIEnv* env, const jni_zero::JavaParamRef<jobject>& root_view); 61 62 // Fetches and returns the input hint from the Android Framework. 63 // 64 // Works as a hint: when unhandled input events are detected, this method 65 // returns |true| with high probability. However, the returned value neither 66 // guarantees presence nor absence of input events in the queue. For example, 67 // this method returns |false| while the singleton is going through 68 // initialization. 69 // 70 // Throttles the calls to one every few milliseconds. When a call is made 71 // before the minimal time interval passed since the previous call, returns 72 // false. 73 static bool HasInput(); 74 75 // RAII override of GetInstance() for testing. 76 struct ScopedOverrideInstance { 77 explicit ScopedOverrideInstance(InputHintChecker* checker); 78 ~ScopedOverrideInstance(); 79 }; 80 81 // Used for UMA metrics to remember that the input hint was used to yield 82 // recently. set_is_after_input_yield(bool after)83 void set_is_after_input_yield(bool after) { is_after_input_yield_ = after; } is_after_input_yield()84 bool is_after_input_yield() { return is_after_input_yield_; } 85 86 // Records the UMA metric based on the InputHintResult. 87 static void RecordInputHintResult(InputHintResult result); 88 89 bool IsInitializedForTesting(); 90 bool FailedToInitializeForTesting(); 91 bool HasInputImplNoThrottlingForTesting(_JNIEnv* env); 92 bool HasInputImplWithThrottlingForTesting(_JNIEnv* env); 93 94 protected: 95 virtual bool HasInputImplWithThrottling(); 96 97 private: 98 friend class base::NoDestructor<InputHintChecker>; 99 class OffThreadInitInvoker; 100 enum class InitState; 101 InitState FetchState() const; 102 void TransitionToState(InitState new_state); 103 void RunOffThreadInitialization(); 104 void InitGlobalRefsAndMethodIds(JNIEnv* env); 105 bool HasInputImpl(JNIEnv* env, jobject o); 106 107 bool is_after_input_yield_ = false; 108 109 // Last time the input hint was requested. Used for throttling. 110 base::TimeTicks last_checked_; 111 112 // Initialization state. It is made atomic because part of the initialization 113 // happens on another thread while public methods of this class can be called 114 // on the UI thread. 115 std::atomic<InitState> init_state_; 116 117 // The android.view.View object reference used to fetch the hint in 118 // HasInput(). 119 JavaObjectWeakGlobalRef view_; 120 121 // Represents a reference to android.view.View.class. Used during 122 // initialization. 123 ScopedJavaGlobalRef<jobject> view_class_; 124 125 // Represents a reference to object of type j.l.reflect.Method for 126 // View#probablyHasInput(). 127 ScopedJavaGlobalRef<jobject> reflect_method_for_has_input_; 128 129 // The ID corresponding to j.l.reflect.Method#invoke(Object, Object...). 130 jmethodID invoke_id_; 131 132 // The ID corresponding to j.l.Boolean#booleanValue(). 133 jmethodID boolean_value_id_; 134 THREAD_CHECKER(thread_checker_); 135 }; 136 137 } // namespace base::android 138 139 #endif // BASE_ANDROID_INPUT_HINT_CHECKER_H_ 140