1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
base / android / input_hint_checker.h [blame]
// 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 <jni.h>
#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<jobject>& 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();
// 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_; }
// Records the UMA metric based on the InputHintResult.
static 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<InputHintChecker>;
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;
// 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<InitState> 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<jobject> view_class_;
// Represents a reference to object of type j.l.reflect.Method for
// View#probablyHasInput().
ScopedJavaGlobalRef<jobject> 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_