144 lines
5.0 KiB
C++
144 lines
5.0 KiB
C++
// 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() { 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<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;
|
|
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<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_
|