• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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