0

Create Android InputReceiver on viz

In this change we are doing the following:
* Create Browser side InputTransferToken when surface changed
  notification is received, and the token is stored along with surface
  variant.
* The input transfer token is packed with surface which is returned when
  GpuProcessCallback.getViewSurface is called.
* On VizCompositor thread we call GpuProcessCallback.getViewSurface
  eventually to get the surface and browser input token required for
  creating input receiver. Note CompositorGpu does already make same
  call as root compositor frame sink creation routine, but it was
  decided to do the same on VizCompositor thread for code simplicity,
  instead of plumbing through SurfaceControl back to InputManager on
  VizCompositor from interfaces on CompositorGpu thread.
* At the moment the change doesn't do anything smart with input receiver
  it just gets deleted after creation. In future changes we will be
  adding callbacks to receive input, and pass it to
  RenderWidgetHostViewAndroid on viz.

Bug: b/364201006
Change-Id: Idec88915a0fe66d0cb1d1c6c6d524483d56b8ff3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5872759
Reviewed-by: Alexander Timin <altimin@chromium.org>
Commit-Queue: Kartar Singh <kartarsingh@google.com>
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Stephen Nusko <nuskos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1366068}
This commit is contained in:
Kartar Singh
2024-10-09 10:32:54 +00:00
committed by Chromium LUCI CQ
parent 1ff4205484
commit a7656a6b30
41 changed files with 731 additions and 58 deletions

@ -29,6 +29,10 @@ AndroidInputReceiverCompat::AndroidInputReceiverCompat() {
dlsym(main_dl_handle, "AInputTransferToken_toJava");
DCHECK(AInputTransferToken_toJavaFn);
*reinterpret_cast<void**>(&AInputTransferToken_releaseFn) =
dlsym(main_dl_handle, "AInputTransferToken_release");
DCHECK(AInputTransferToken_releaseFn);
*reinterpret_cast<void**>(&AInputEvent_toJavaFn) =
dlsym(main_dl_handle, "AInputEvent_toJava");
DCHECK(AInputEvent_toJavaFn);
@ -37,6 +41,10 @@ AndroidInputReceiverCompat::AndroidInputReceiverCompat() {
dlsym(main_dl_handle, "AInputReceiverCallbacks_create");
DCHECK(AInputReceiverCallbacks_createFn);
*reinterpret_cast<void**>(&AInputReceiverCallbacks_releaseFn) =
dlsym(main_dl_handle, "AInputReceiverCallbacks_release");
DCHECK(AInputReceiverCallbacks_releaseFn);
*reinterpret_cast<void**>(&AInputReceiverCallbacks_setMotionEventCallbackFn) =
dlsym(main_dl_handle, "AInputReceiverCallbacks_setMotionEventCallback");
DCHECK(AInputReceiverCallbacks_setMotionEventCallbackFn);
@ -48,6 +56,10 @@ AndroidInputReceiverCompat::AndroidInputReceiverCompat() {
*reinterpret_cast<void**>(&AInputReceiver_getInputTransferTokenFn) =
dlsym(main_dl_handle, "AInputReceiver_getInputTransferToken");
DCHECK(AInputReceiver_getInputTransferTokenFn);
*reinterpret_cast<void**>(&AInputReceiver_releaseFn) =
dlsym(main_dl_handle, "AInputReceiver_release");
DCHECK(AInputReceiver_releaseFn);
}
// static

@ -23,9 +23,13 @@ using pAInputTransferToken_fromJava = AInputTransferToken* (*)(JNIEnv*,
jobject);
using pAInputTransferToken_toJava = jobject (*)(JNIEnv*,
const AInputTransferToken*);
using pAInputTransferToken_release =
void (*)(AInputTransferToken* aInputTransferToken);
using pAInputEvent_toJava = jobject (*)(JNIEnv*, const AInputEvent*);
using pAInputReceiverCallbacks_create =
AInputReceiverCallbacks* (*)(void* context);
using pAInputReceiverCallbacks_release =
void (*)(AInputReceiverCallbacks* callbacks);
using pAInputReceiverCallbacks_setMotionEventCallback =
void (*)(AInputReceiverCallbacks*, AInputReceiver_onMotionEvent);
using pAInputReceiver_createUnbatchedInputReceiver =
@ -35,6 +39,7 @@ using pAInputReceiver_createUnbatchedInputReceiver =
AInputReceiverCallbacks*);
using pAInputReceiver_getInputTransferToken =
AInputTransferToken* (*)(AInputReceiver*);
using pAInputReceiver_release = void (*)(AInputReceiver*);
} // extern "C"
@ -54,13 +59,16 @@ class BASE_EXPORT AndroidInputReceiverCompat {
pAInputTransferToken_fromJava AInputTransferToken_fromJavaFn;
pAInputTransferToken_toJava AInputTransferToken_toJavaFn;
pAInputTransferToken_release AInputTransferToken_releaseFn;
pAInputEvent_toJava AInputEvent_toJavaFn;
pAInputReceiverCallbacks_create AInputReceiverCallbacks_createFn;
pAInputReceiverCallbacks_release AInputReceiverCallbacks_releaseFn;
pAInputReceiverCallbacks_setMotionEventCallback
AInputReceiverCallbacks_setMotionEventCallbackFn;
pAInputReceiver_createUnbatchedInputReceiver
AInputReceiver_createUnbatchedInputReceiverFn;
pAInputReceiver_getInputTransferToken AInputReceiver_getInputTransferTokenFn;
pAInputReceiver_release AInputReceiver_releaseFn;
private:
AndroidInputReceiverCompat();

@ -22,11 +22,14 @@ TEST(AndroidInputReceiverCompatTest, CanFindMethodsOnAndroidVPlus) {
EXPECT_NE(instance.AInputTransferToken_fromJavaFn, nullptr);
EXPECT_NE(instance.AInputTransferToken_toJavaFn, nullptr);
EXPECT_NE(instance.AInputTransferToken_releaseFn, nullptr);
EXPECT_NE(instance.AInputEvent_toJavaFn, nullptr);
EXPECT_NE(instance.AInputReceiverCallbacks_createFn, nullptr);
EXPECT_NE(instance.AInputReceiverCallbacks_releaseFn, nullptr);
EXPECT_NE(instance.AInputReceiverCallbacks_setMotionEventCallbackFn, nullptr);
EXPECT_NE(instance.AInputReceiver_createUnbatchedInputReceiverFn, nullptr);
EXPECT_NE(instance.AInputReceiver_getInputTransferTokenFn, nullptr);
EXPECT_NE(instance.AInputReceiver_releaseFn, nullptr);
}
} // namespace base

@ -13,9 +13,11 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.AttachedSurfaceControl;
import android.view.Surface;
import android.view.View;
import android.widget.FrameLayout;
import android.window.InputTransferToken;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
@ -37,6 +39,7 @@ import org.chromium.chrome.browser.tab_ui.TabContentManager;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.InputUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.resources.AndroidResourceType;
import org.chromium.ui.resources.ResourceManager;
@ -473,6 +476,13 @@ public class CompositorView extends FrameLayout
}
if (mNativeCompositorView == 0) return;
InputTransferToken browserInputToken = null;
if (InputUtils.isTransferInputToVizSupported()) {
AttachedSurfaceControl rootSurfaceControl =
((Activity) getContext()).getWindow().getRootSurfaceControl();
browserInputToken = rootSurfaceControl.getInputTransferToken();
}
CompositorViewJni.get()
.surfaceChanged(
mNativeCompositorView,
@ -481,7 +491,8 @@ public class CompositorView extends FrameLayout
width,
height,
canUseSurfaceControl(),
surface);
surface,
browserInputToken);
mRenderHost.onSurfaceResized(width, height);
}
@ -792,7 +803,8 @@ public class CompositorView extends FrameLayout
int width,
int height,
boolean backedBySurfaceTexture,
Surface surface);
Surface surface,
InputTransferToken browserInputToken);
void onPhysicalBackingSizeChanged(
long nativeCompositorView,

@ -123,7 +123,7 @@ base::android::ScopedJavaLocalRef<jobject> CompositorView::GetResourceManager(
void CompositorView::RecreateSurface() {
JNIEnv* env = base::android::AttachCurrentThread();
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
Java_CompositorView_recreateSurface(env, obj_);
}
@ -164,22 +164,25 @@ void CompositorView::SurfaceCreated(JNIEnv* env,
void CompositorView::SurfaceDestroyed(JNIEnv* env,
const JavaParamRef<jobject>& object) {
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
current_surface_format_ = 0;
tab_content_manager_->OnUIResourcesWereEvicted();
}
void CompositorView::SurfaceChanged(JNIEnv* env,
const JavaParamRef<jobject>& object,
jint format,
jint width,
jint height,
bool can_be_used_with_surface_control,
const JavaParamRef<jobject>& surface) {
void CompositorView::SurfaceChanged(
JNIEnv* env,
const JavaParamRef<jobject>& object,
jint format,
jint width,
jint height,
bool can_be_used_with_surface_control,
const JavaParamRef<jobject>& surface,
const JavaParamRef<jobject>& browser_input_token) {
DCHECK(surface);
if (current_surface_format_ != format) {
current_surface_format_ = format;
compositor_->SetSurface(surface, can_be_used_with_surface_control);
compositor_->SetSurface(surface, can_be_used_with_surface_control,
browser_input_token);
}
gfx::Size size = gfx::Size(width, height);
compositor_->SetWindowBounds(size);
@ -347,7 +350,7 @@ void CompositorView::BrowserChildProcessKilled(
base::android::SDK_VERSION_R &&
data.process_type == content::PROCESS_TYPE_GPU) {
JNIEnv* env = base::android::AttachCurrentThread();
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
Java_CompositorView_recreateSurface(env, obj_);
}
}

@ -64,13 +64,15 @@ class CompositorView : public content::CompositorClient,
const base::android::JavaParamRef<jobject>& object);
void SurfaceDestroyed(JNIEnv* env,
const base::android::JavaParamRef<jobject>& object);
void SurfaceChanged(JNIEnv* env,
const base::android::JavaParamRef<jobject>& object,
jint format,
jint width,
jint height,
bool can_be_used_with_surface_control,
const base::android::JavaParamRef<jobject>& surface);
void SurfaceChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& object,
jint format,
jint width,
jint height,
bool can_be_used_with_surface_control,
const base::android::JavaParamRef<jobject>& surface,
const base::android::JavaParamRef<jobject>& browser_input_token);
void OnPhysicalBackingSizeChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,

@ -7,17 +7,21 @@ package org.chromium.components.embedder_support.view;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.view.AttachedSurfaceControl;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.FrameLayout;
import android.window.InputTransferToken;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.InputUtils;
import org.chromium.ui.base.WindowAndroid;
/***
@ -75,6 +79,14 @@ public class ContentViewRenderView extends FrameLayout {
public void surfaceChanged(
SurfaceHolder holder, int format, int width, int height) {
assert mNativeContentViewRenderView != 0;
InputTransferToken hostInputToken = null;
Window window = mWindowAndroid.getWindow();
if (InputUtils.isTransferInputToVizSupported() && window != null) {
AttachedSurfaceControl rootSurfaceControl =
window.getRootSurfaceControl();
hostInputToken = rootSurfaceControl.getInputTransferToken();
}
ContentViewRenderViewJni.get()
.surfaceChanged(
mNativeContentViewRenderView,
@ -82,7 +94,8 @@ public class ContentViewRenderView extends FrameLayout {
format,
width,
height,
holder.getSurface());
holder.getSurface(),
hostInputToken);
if (mWebContents != null) {
ContentViewRenderViewJni.get()
.onPhysicalBackingSizeChanged(
@ -299,7 +312,8 @@ public class ContentViewRenderView extends FrameLayout {
int format,
int width,
int height,
Surface surface);
Surface surface,
InputTransferToken browserInputToken);
void setOverlayVideoMode(
long nativeContentViewRenderView, ContentViewRenderView caller, boolean enabled);

@ -92,7 +92,7 @@ void ContentViewRenderView::SurfaceDestroyed(JNIEnv* env,
// detached and freed by OS.
compositor_->PreserveChildSurfaceControls();
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
current_surface_format_ = 0;
}
@ -102,11 +102,13 @@ void ContentViewRenderView::SurfaceChanged(
jint format,
jint width,
jint height,
const JavaParamRef<jobject>& surface) {
const JavaParamRef<jobject>& surface,
const JavaParamRef<jobject>& browser_input_token) {
if (current_surface_format_ != format) {
current_surface_format_ = format;
compositor_->SetSurface(surface,
true /* can_be_used_with_surface_control */);
true /* can_be_used_with_surface_control */,
browser_input_token);
}
compositor_->SetWindowBounds(gfx::Size(width, height));
}

@ -42,12 +42,14 @@ class ContentViewRenderView : public content::CompositorClient {
const base::android::JavaParamRef<jobject>& obj);
void SurfaceDestroyed(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
void SurfaceChanged(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint format,
jint width,
jint height,
const base::android::JavaParamRef<jobject>& surface);
void SurfaceChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint format,
jint width,
jint height,
const base::android::JavaParamRef<jobject>& surface,
const base::android::JavaParamRef<jobject>& browser_input_token);
void SetOverlayVideoMode(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
bool enabled);

@ -100,6 +100,12 @@ component("input") {
}
if (is_android) {
sources += [
"android/scoped_input_receiver.cc",
"android/scoped_input_receiver.h",
"android/scoped_input_receiver_callbacks.cc",
"android/scoped_input_receiver_callbacks.h",
"android/scoped_input_transfer_token.cc",
"android/scoped_input_transfer_token.h",
"native_web_keyboard_event_android.cc",
"web_input_event_builders_android.cc",
"web_input_event_builders_android.h",

@ -0,0 +1,52 @@
// 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.
#include "components/input/android/scoped_input_receiver.h"
#include "base/android/android_input_receiver_compat.h"
#include "base/check.h"
namespace input {
ScopedInputReceiver::ScopedInputReceiver(ALooper* looper,
AInputTransferToken* input_token,
ASurfaceControl* surface_control,
AInputReceiverCallbacks* callbacks) {
CHECK(base::AndroidInputReceiverCompat::IsSupportAvailable());
a_input_receiver_ = base::AndroidInputReceiverCompat::GetInstance()
.AInputReceiver_createUnbatchedInputReceiverFn(
looper, input_token, surface_control, callbacks);
}
ScopedInputReceiver::~ScopedInputReceiver() {
DestroyIfNeeded();
}
ScopedInputReceiver::ScopedInputReceiver(ScopedInputReceiver&& other)
: a_input_receiver_(other.a_input_receiver_) {
other.a_input_receiver_ = nullptr;
}
ScopedInputReceiver& ScopedInputReceiver::operator=(
ScopedInputReceiver&& other) {
if (this != &other) {
DestroyIfNeeded();
a_input_receiver_ = other.a_input_receiver_;
other.a_input_receiver_ = nullptr;
}
return *this;
}
void ScopedInputReceiver::DestroyIfNeeded() {
if (a_input_receiver_ == nullptr) {
return;
}
// Not calling release due to AOSP crash on calling the API -
// b/368251173.
// base::AndroidInputReceiverCompat::GetInstance()
// .AInputReceiver_releaseFn(a_input_receiver_);
a_input_receiver_ = nullptr;
}
} // namespace input

@ -0,0 +1,54 @@
// 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 COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_H_
#define COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_H_
#include "base/component_export.h"
#include "base/memory/raw_ptr_exclusion.h"
struct AInputReceiver;
struct ALooper;
struct AInputTransferToken;
struct ASurfaceControl;
struct AInputReceiverCallbacks;
namespace input {
// Class to manage lifecycle of AInputReceiver. AInputReceiver was
// added in Android V+, this class is expected to be instantiated only when
// running on Android V+. AInputReceiver allows receiving input through
// registered callbacks. NDK documentation for these APIs is not yet live but we
// can find the relevant methods and comments here:
// https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/include/android/surface_control_input_receiver.h
class COMPONENT_EXPORT(INPUT) ScopedInputReceiver {
public:
ScopedInputReceiver(ALooper* looper,
AInputTransferToken* input_token,
ASurfaceControl* surface_control,
AInputReceiverCallbacks* callbacks);
~ScopedInputReceiver();
ScopedInputReceiver(ScopedInputReceiver&& other);
ScopedInputReceiver& operator=(ScopedInputReceiver&& other);
// Move only type.
ScopedInputReceiver(const ScopedInputReceiver&) = delete;
ScopedInputReceiver& operator=(const ScopedInputReceiver&) = delete;
explicit operator bool() const { return !!a_input_receiver_; }
AInputReceiver* a_input_receiver() const { return a_input_receiver_; }
private:
void DestroyIfNeeded();
// RAW_PTR_EXCLUSION: #global-scope
RAW_PTR_EXCLUSION AInputReceiver* a_input_receiver_ = nullptr;
};
} // namespace input
#endif // COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_H_

@ -0,0 +1,47 @@
// 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.
#include "components/input/android/scoped_input_receiver_callbacks.h"
#include "base/android/android_input_receiver_compat.h"
#include "base/check.h"
namespace input {
ScopedInputReceiverCallbacks::ScopedInputReceiverCallbacks(void* context) {
CHECK(base::AndroidInputReceiverCompat::IsSupportAvailable());
a_input_receiver_callbacks_ = base::AndroidInputReceiverCompat::GetInstance()
.AInputReceiverCallbacks_createFn(context);
}
ScopedInputReceiverCallbacks::~ScopedInputReceiverCallbacks() {
DestroyIfNeeded();
}
ScopedInputReceiverCallbacks::ScopedInputReceiverCallbacks(
ScopedInputReceiverCallbacks&& other)
: a_input_receiver_callbacks_(other.a_input_receiver_callbacks_) {
other.a_input_receiver_callbacks_ = nullptr;
}
ScopedInputReceiverCallbacks& ScopedInputReceiverCallbacks::operator=(
ScopedInputReceiverCallbacks&& other) {
if (this != &other) {
DestroyIfNeeded();
a_input_receiver_callbacks_ = other.a_input_receiver_callbacks_;
other.a_input_receiver_callbacks_ = nullptr;
}
return *this;
}
void ScopedInputReceiverCallbacks::DestroyIfNeeded() {
if (a_input_receiver_callbacks_ == nullptr) {
return;
}
base::AndroidInputReceiverCompat::GetInstance()
.AInputReceiverCallbacks_releaseFn(a_input_receiver_callbacks_);
a_input_receiver_callbacks_ = nullptr;
}
} // namespace input

@ -0,0 +1,48 @@
// 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 COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_CALLBACKS_H_
#define COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_CALLBACKS_H_
#include "base/component_export.h"
#include "base/memory/raw_ptr_exclusion.h"
struct AInputReceiverCallbacks;
namespace input {
// Class to manage lifecycle of AInputReceiverCallbacks. AInputReceiverCallbacks
// was added in Android V+ and the class is expected to be instantiated only
// when running on Android V+.
class COMPONENT_EXPORT(INPUT) ScopedInputReceiverCallbacks {
public:
explicit ScopedInputReceiverCallbacks(void* context);
~ScopedInputReceiverCallbacks();
ScopedInputReceiverCallbacks(ScopedInputReceiverCallbacks&& other);
ScopedInputReceiverCallbacks& operator=(ScopedInputReceiverCallbacks&& other);
// Move only type.
ScopedInputReceiverCallbacks(const ScopedInputReceiverCallbacks&) = delete;
ScopedInputReceiverCallbacks& operator=(const ScopedInputReceiverCallbacks&) =
delete;
explicit operator bool() const { return !!a_input_receiver_callbacks_; }
AInputReceiverCallbacks* a_input_receiver_callbacks() const {
return a_input_receiver_callbacks_;
}
private:
void DestroyIfNeeded();
// RAW_PTR_EXCLUSION: #global-scope
RAW_PTR_EXCLUSION AInputReceiverCallbacks* a_input_receiver_callbacks_ =
nullptr;
};
} // namespace input
#endif // COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_RECEIVER_CALLBACKS_H_

@ -0,0 +1,50 @@
// 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.
#include "components/input/android/scoped_input_transfer_token.h"
#include "base/android/android_input_receiver_compat.h"
#include "base/android/jni_android.h"
namespace input {
ScopedInputTransferToken::ScopedInputTransferToken(
const jobject& input_transfer_token) {
CHECK(base::AndroidInputReceiverCompat::IsSupportAvailable());
JNIEnv* env = base::android::AttachCurrentThread();
a_input_transfer_token_ =
base::AndroidInputReceiverCompat::GetInstance()
.AInputTransferToken_fromJavaFn(env, input_transfer_token);
}
ScopedInputTransferToken::~ScopedInputTransferToken() {
DestroyIfNeeded();
}
ScopedInputTransferToken::ScopedInputTransferToken(
ScopedInputTransferToken&& other)
: a_input_transfer_token_(other.a_input_transfer_token_) {
other.a_input_transfer_token_ = nullptr;
}
ScopedInputTransferToken& ScopedInputTransferToken::operator=(
ScopedInputTransferToken&& other) {
if (this != &other) {
DestroyIfNeeded();
a_input_transfer_token_ = other.a_input_transfer_token_;
other.a_input_transfer_token_ = nullptr;
}
return *this;
}
void ScopedInputTransferToken::DestroyIfNeeded() {
if (a_input_transfer_token_ == nullptr) {
return;
}
base::AndroidInputReceiverCompat::GetInstance().AInputTransferToken_releaseFn(
a_input_transfer_token_);
a_input_transfer_token_ = nullptr;
}
} // namespace input

@ -0,0 +1,50 @@
// 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 COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_TRANSFER_TOKEN_H_
#define COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_TRANSFER_TOKEN_H_
#include <jni.h>
#include "base/component_export.h"
#include "base/memory/raw_ptr_exclusion.h"
struct AInputTransferToken;
namespace input {
// Class to manage lifecycle of AInputTransferToken. AInputTransferToken was
// added in Android V+ and the class is expected to be instantiated only when
// running on Android V+.
class COMPONENT_EXPORT(INPUT) ScopedInputTransferToken {
public:
explicit ScopedInputTransferToken(const jobject& input_transfer_token);
~ScopedInputTransferToken();
ScopedInputTransferToken(ScopedInputTransferToken&& other);
ScopedInputTransferToken& operator=(ScopedInputTransferToken&& other);
// Move only type.
ScopedInputTransferToken(const ScopedInputTransferToken&) = delete;
ScopedInputTransferToken& operator=(const ScopedInputTransferToken&) = delete;
explicit operator bool() const { return !!a_input_transfer_token_; }
AInputTransferToken* a_input_transfer_token() const {
return a_input_transfer_token_;
}
private:
explicit ScopedInputTransferToken(AInputTransferToken* a_native_window);
void DestroyIfNeeded();
// RAW_PTR_EXCLUSION: #global-scope
RAW_PTR_EXCLUSION AInputTransferToken* a_input_transfer_token_ = nullptr;
};
} // namespace input
#endif // COMPONENTS_INPUT_ANDROID_SCOPED_INPUT_TRANSFER_TOKEN_H_

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_trace_processor.h"
#include "components/input/features.h"
@ -134,6 +135,28 @@ IN_PROC_BROWSER_TEST_P(AndroidInputBrowserTest,
}
}
IN_PROC_BROWSER_TEST_P(AndroidInputBrowserTest, AndroidInputReceiverCreated) {
base::HistogramTester histogram_tester;
content::RenderFrameSubmissionObserver render_frame_submission_observer(
shell()->web_contents());
EXPECT_TRUE(content::NavigateToURL(
shell(), GURL("data:text/html,<!doctype html>"
"<body style='background-color: magenta;'></body>")));
if (render_frame_submission_observer.render_frame_count() == 0) {
render_frame_submission_observer.WaitForAnyFrameSubmission();
}
// By this point a root compositor frame sink should have been created as
// well, and if an input receiver were to be created it should have been
// since it happens when root compositor frame sink is created.
const int expected_count = IsTransferInputToVizSupported() ? 1 : 0;
content::FetchHistogramsFromChildProcesses();
histogram_tester.ExpectUniqueSample(
"Android.InputOnViz.InputReceiverCreationResult",
/*kSuccessfullyCreated*/ 0, expected_count);
}
INSTANTIATE_TEST_SUITE_P(All,
AndroidInputBrowserTest,
::testing::Bool(),

@ -85,7 +85,7 @@ void CompositorViewImpl::SurfaceDestroyed(JNIEnv* env,
// detached and freed by OS.
compositor_->PreserveChildSurfaceControls();
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
current_surface_format_ = kPixelFormatUnknown;
}
@ -99,7 +99,7 @@ void CompositorViewImpl::SurfaceChanged(JNIEnv* env,
DCHECK(surface);
if (current_surface_format_ != format) {
current_surface_format_ = format;
compositor_->SetSurface(surface, can_be_used_with_surface_control);
compositor_->SetSurface(surface, can_be_used_with_surface_control, nullptr);
}
gfx::Size content_size(width, height);
@ -125,7 +125,7 @@ void CompositorViewImpl::SetRootLayer(scoped_refptr<cc::slim::Layer> layer) {
void CompositorViewImpl::RecreateSurface() {
JNIEnv* env = base::android::AttachCurrentThread();
compositor_->SetSurface(nullptr, false);
compositor_->SetSurface(nullptr, false, nullptr);
Java_CompositorViewImpl_recreateSurface(env, obj_);
}

@ -3,3 +3,10 @@
include_rules = [
"+components/viz/service/frame_sinks",
]
specific_include_rules = {
"input_manager.cc": [
"+gpu/ipc/common/gpu_surface_lookup.h",
"+ui/gl/android/scoped_a_native_window.h",
],
}

@ -4,11 +4,25 @@
#include "components/viz/service/input/input_manager.h"
#if BUILDFLAG(IS_ANDROID)
#include <android/looper.h>
#endif // BUILDFLAG(IS_ANDROID)
#include <utility>
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_macros.h"
#include "components/viz/service/input/render_input_router_delegate_impl.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/input/android/scoped_input_receiver.h"
#include "components/input/android/scoped_input_receiver_callbacks.h"
#include "components/input/android/scoped_input_transfer_token.h"
#include "gpu/ipc/common/gpu_surface_lookup.h"
#include "ui/gfx/android/android_surface_control_compat.h"
#include "ui/gl/android/scoped_a_native_window.h"
#endif // BUILDFLAG(IS_ANDROID)
namespace viz {
FrameSinkMetadata::FrameSinkMetadata(
@ -21,6 +35,29 @@ FrameSinkMetadata::FrameSinkMetadata(FrameSinkMetadata&& other) = default;
FrameSinkMetadata& FrameSinkMetadata::operator=(FrameSinkMetadata&& other) =
default;
namespace {
#if BUILDFLAG(IS_ANDROID)
constexpr char kInputSurfaceControlName[] = "ChromeInputSurfaceControl";
constexpr char kInputReceiverCreationResultHistogram[] =
"Android.InputOnViz.InputReceiverCreationResult";
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class CreateAndroidInputReceiverResult {
kSuccessfullyCreated = 0,
kFailedUnknown = 1,
kFailedNullSurfaceControl = 2,
kFailedNullLooper = 3,
kFailedNullInputTransferToken = 4,
kFailedNullCallbacks = 5,
kMaxValue = kFailedNullCallbacks,
};
#endif // BUILDFLAG(IS_ANDROID)
} // namespace
InputManager::~InputManager() {
frame_sink_manager_->RemoveObserver(this);
}
@ -41,6 +78,7 @@ void InputManager::OnCreateCompositorFrameSink(
TRACE_EVENT("viz", "InputManager::OnCreateCompositorFrameSink",
"config_is_null", !render_input_router_config, "frame_sink_id",
frame_sink_id);
#if BUILDFLAG(IS_ANDROID)
if (create_input_receiver) {
CHECK(is_root);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
@ -49,6 +87,7 @@ void InputManager::OnCreateCompositorFrameSink(
surface_handle));
return;
}
#endif // BUILDFLAG(IS_ANDROID)
// `render_input_router_config` is non null only when layer tree frame sinks
// for renderer are being requested.
@ -122,10 +161,75 @@ input::TouchEmulator* InputManager::GetTouchEmulator(bool create_if_necessary) {
return nullptr;
}
#if BUILDFLAG(IS_ANDROID)
void InputManager::CreateAndroidInputReceiver(
const FrameSinkId& frame_sink_id,
const gpu::SurfaceHandle& surface_handle) {
// TODO(b/364201006): Request android to create input receiver.
// This results in a sync binder to Browser, the same call is made on
// CompositorGpu thread as well but to keep the code simple and not having to
// plumb through Android SurfaceControl and InputTransferToken, this duplicate
// call is made from here.
auto surface_record =
gpu::GpuSurfaceLookup::GetInstance()->AcquireJavaSurface(surface_handle);
CHECK(absl::holds_alternative<gl::ScopedJavaSurface>(
surface_record.surface_variant));
gl::ScopedJavaSurface& scoped_java_surface =
absl::get<gl::ScopedJavaSurface>(surface_record.surface_variant);
gl::ScopedANativeWindow window(scoped_java_surface);
scoped_refptr<gfx::SurfaceControl::Surface> surface =
base::MakeRefCounted<gfx::SurfaceControl::Surface>(
window.a_native_window(), kInputSurfaceControlName);
if (!surface->surface()) {
UMA_HISTOGRAM_ENUMERATION(
kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kFailedNullSurfaceControl);
return;
}
ALooper* looper = ALooper_prepare(0);
if (!looper) {
UMA_HISTOGRAM_ENUMERATION(
kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kFailedNullLooper);
return;
}
CHECK(surface_record.host_input_token);
input::ScopedInputTransferToken browser_input_token(
surface_record.host_input_token.obj());
if (!browser_input_token) {
UMA_HISTOGRAM_ENUMERATION(
kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kFailedNullInputTransferToken);
return;
}
// Creation of InputReceiverCallbacks with a null context is supported.
// TODO(b/364201006): Implement InputReceiverCallbacks for passing input
// events to InputManager and pass a non-null context while creation.
input::ScopedInputReceiverCallbacks callbacks(/*context=*/nullptr);
if (!callbacks) {
UMA_HISTOGRAM_ENUMERATION(
kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kFailedNullCallbacks);
return;
}
input::ScopedInputReceiver receiver(
looper, browser_input_token.a_input_transfer_token(), surface->surface(),
callbacks.a_input_receiver_callbacks());
if (receiver) {
UMA_HISTOGRAM_ENUMERATION(
kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kSuccessfullyCreated);
} else {
UMA_HISTOGRAM_ENUMERATION(kInputReceiverCreationResultHistogram,
CreateAndroidInputReceiverResult::kFailedUnknown);
}
}
#endif // BUILDFLAG(IS_ANDROID)
} // namespace viz

@ -67,8 +67,10 @@ class VIZ_SERVICE_EXPORT InputManager
input::TouchEmulator* GetTouchEmulator(bool create_if_necessary) override;
private:
#if BUILDFLAG(IS_ANDROID)
void CreateAndroidInputReceiver(const FrameSinkId& frame_sink_id,
const gpu::SurfaceHandle& surface_handle);
#endif // BUILDFLAG(IS_ANDROID)
friend class MockInputManager;

@ -85,8 +85,10 @@ class ChildProcessSurfaceManager : public gpu::ScopedSurfaceRequestConduit,
content::JNI_SurfaceWrapper_takeSurface(env, surface_wrapper),
/*auto_release=*/true);
DCHECK(!surface.j_surface().is_null());
return gpu::SurfaceRecord(std::move(surface),
can_be_used_with_surface_control);
return gpu::SurfaceRecord(
std::move(surface), can_be_used_with_surface_control,
content::JNI_SurfaceWrapper_getBrowserInputToken(env,
surface_wrapper));
} else {
gl::ScopedJavaSurfaceControl surface_control(
JNI_SurfaceWrapper_takeSurfaceControl(env, surface_wrapper),

@ -5,6 +5,7 @@
#include "base/android/feature_map.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "components/input/features.h"
#include "content/common/features.h"
#include "content/public/common/content_features.h"
#include "third_party/blink/public/common/features.h"
@ -26,6 +27,7 @@ const base::Feature* const kFeaturesExposedToJava[] = {
&blink::features::kStylusPointerAdjustment,
&blink::features::kStylusRichGestures,
&blink::features::kViewportSegments,
&input::features::kInputOnViz,
&features::kAccessibilityIncludeLongClickAction,
&features::kAccessibilityPageZoom,
&features::kAccessibilityPageZoomEnhancements,

@ -41,9 +41,18 @@ JNI_GpuProcessCallback_GetViewSurface(
absl::visit(
base::Overloaded{[&](gl::ScopedJavaSurface&& scoped_java_surface) {
if (!scoped_java_surface.IsEmpty()) {
j_surface_wrapper = JNI_SurfaceWrapper_create(
env, scoped_java_surface.j_surface(),
surface_record.can_be_used_with_surface_control);
if (surface_record.host_input_token) {
j_surface_wrapper = JNI_SurfaceWrapper_create(
env, scoped_java_surface.j_surface(),
surface_record
.can_be_used_with_surface_control,
surface_record.host_input_token);
} else {
j_surface_wrapper = JNI_SurfaceWrapper_create(
env, scoped_java_surface.j_surface(),
surface_record
.can_be_used_with_surface_control);
}
}
},
[&](gl::ScopedJavaSurfaceControl&& surface_control) {

@ -43,6 +43,7 @@
#include "cc/slim/layer_tree.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_settings.h"
#include "components/input/utils.h"
#include "components/viz/common/features.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/quads/compositor_frame.h"
@ -243,7 +244,7 @@ CompositorImpl::~CompositorImpl() {
DetachRootWindow();
// Clean-up any surface references.
SetSurface(nullptr, false);
SetSurface(nullptr, false, nullptr);
BrowserGpuChannelHostFactory::instance()->MaybeCloseChannel();
}
@ -303,8 +304,10 @@ void CompositorImpl::SetRootLayer(scoped_refptr<cc::slim::Layer> root_layer) {
}
}
void CompositorImpl::SetSurface(const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control) {
void CompositorImpl::SetSurface(
const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control,
const base::android::JavaRef<jobject>& host_input_token) {
gpu::GpuSurfaceTracker* tracker = gpu::GpuSurfaceTracker::Get();
if (window_) {
@ -321,8 +324,9 @@ void CompositorImpl::SetSurface(const base::android::JavaRef<jobject>& surface,
if (window) {
window_ = std::move(window);
// Register first, SetVisible() might create a LayerTreeFrameSink.
surface_handle_ = tracker->AddSurfaceForNativeWidget(gpu::SurfaceRecord(
std::move(scoped_surface), can_be_used_with_surface_control));
surface_handle_ = tracker->AddSurfaceForNativeWidget(
gpu::SurfaceRecord(std::move(scoped_surface),
can_be_used_with_surface_control, host_input_token));
SetVisible(true);
}
}
@ -778,6 +782,9 @@ void CompositorImpl::InitializeVizLayerTreeFrameSink(
root_params->gpu_compositing = true;
root_params->renderer_settings = renderer_settings;
root_params->refresh_rate = root_window_->GetRefreshRate();
if (input::IsTransferInputToVizSupported()) {
root_params->create_input_receiver = true;
}
GetHostFrameSinkManager()->CreateRootCompositorFrameSink(
std::move(root_params));
@ -827,7 +834,7 @@ void CompositorImpl::OnFatalOrSurfaceContextCreationFailure(
}
if (context_result == gpu::ContextResult::kSurfaceFailure) {
SetSurface(nullptr, false);
SetSurface(nullptr, false, nullptr);
client_->RecreateSurface();
}
}

@ -109,8 +109,10 @@ class CONTENT_EXPORT CompositorImpl : public Compositor,
// Compositor implementation.
void SetRootWindow(gfx::NativeWindow root_window) override;
void SetRootLayer(scoped_refptr<cc::slim::Layer> root) override;
void SetSurface(const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control) override;
void SetSurface(
const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control,
const base::android::JavaRef<jobject>& host_input_token) override;
void SetBackgroundColor(int color) override;
void SetWindowBounds(const gfx::Size& size) override;
const gfx::Size& GetWindowBounds() override;

@ -11,6 +11,15 @@
namespace content {
base::android::ScopedJavaLocalRef<jobject> JNI_SurfaceWrapper_create(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
jboolean canBeUsedWithSurfaceControl,
const base::android::JavaRef<jobject>& browserInputToken) {
return Java_SurfaceWrapper_create(env, surface, canBeUsedWithSurfaceControl,
browserInputToken);
}
base::android::ScopedJavaLocalRef<jobject> JNI_SurfaceWrapper_create(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
@ -55,4 +64,11 @@ JNI_SurfaceWrapper_takeSurfaceControl(
return Java_SurfaceWrapper_takeSurfaceControl(env, obj);
}
base::android::ScopedJavaLocalRef<jobject>
JNI_SurfaceWrapper_getBrowserInputToken(
JNIEnv* env,
const base::android::JavaRef<jobject>& obj) {
return Java_SurfaceWrapper_getBrowserInputToken(env, obj);
}
} // namespace content.

@ -10,6 +10,12 @@
namespace content {
base::android::ScopedJavaLocalRef<jobject> JNI_SurfaceWrapper_create(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
jboolean canBeUsedWithSurfaceControl,
const base::android::JavaRef<jobject>& browserInputToken);
base::android::ScopedJavaLocalRef<jobject> JNI_SurfaceWrapper_create(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
@ -37,6 +43,11 @@ JNI_SurfaceWrapper_takeSurfaceControl(
JNIEnv* env,
const base::android::JavaRef<jobject>& obj);
base::android::ScopedJavaLocalRef<jobject>
JNI_SurfaceWrapper_getBrowserInputToken(
JNIEnv* env,
const base::android::JavaRef<jobject>& obj);
} // namespace content
#endif // CONTENT_COMMON_ANDROID_SURFACE_WRAPPER_H_

@ -419,6 +419,7 @@ android_library("content_full_java") {
"java/src/org/chromium/content_public/browser/WebContentsStatics.java",
"java/src/org/chromium/content_public/browser/selection/SelectionActionMenuDelegate.java",
"java/src/org/chromium/content_public/browser/selection/SelectionDropdownMenuDelegate.java",
"java/src/org/chromium/content_public/common/InputUtils.java",
]
}

@ -4,11 +4,13 @@
package org.chromium.content.common;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.InputTransferToken;
import androidx.annotation.RequiresApi;
@ -22,12 +24,23 @@ public class SurfaceWrapper implements Parcelable {
private Surface mSurface;
private final boolean mCanBeUsedWithSurfaceControl;
private SurfaceControl mSurfaceControl;
private InputTransferToken mBrowserInputToken;
private SurfaceWrapper(Surface surface, boolean canBeUsedWithSurfaceControl) {
mWrapsSurface = true;
mSurface = surface;
mCanBeUsedWithSurfaceControl = canBeUsedWithSurfaceControl;
mSurfaceControl = null;
mBrowserInputToken = null;
}
private SurfaceWrapper(
Surface surface,
boolean canBeUsedWithSurfaceControl,
InputTransferToken browserInputToken) {
this(surface, canBeUsedWithSurfaceControl);
assert browserInputToken != null;
mBrowserInputToken = browserInputToken;
}
@RequiresApi(Build.VERSION_CODES.Q)
@ -36,6 +49,12 @@ public class SurfaceWrapper implements Parcelable {
mSurface = null;
mCanBeUsedWithSurfaceControl = true;
mSurfaceControl = surfaceControl;
mBrowserInputToken = null;
}
@CalledByNative
public InputTransferToken getBrowserInputToken() {
return mBrowserInputToken;
}
@CalledByNative
@ -69,19 +88,40 @@ public class SurfaceWrapper implements Parcelable {
return 0;
}
@SuppressLint("NewApi")
private void writeInputTokenToParcel(Parcel out) {
assert mBrowserInputToken != null;
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
mBrowserInputToken.writeToParcel(out, 0);
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mWrapsSurface ? 1 : 0);
if (mWrapsSurface) {
boolean hasBrowserInputToken = mBrowserInputToken != null;
out.writeInt(hasBrowserInputToken ? 1 : 0);
// Ignore flags so that the Surface won't call release()
mSurface.writeToParcel(out, 0);
out.writeInt(mCanBeUsedWithSurfaceControl ? 1 : 0);
if (hasBrowserInputToken) {
writeInputTokenToParcel(out);
}
} else {
// Ignore flags so that SurfaceControl won't call release().
mSurfaceControl.writeToParcel(out, 0);
}
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@CalledByNative
private static SurfaceWrapper create(
Surface surface,
boolean canBeUsedWithSurfaceControl,
InputTransferToken browserInputToken) {
return new SurfaceWrapper(surface, canBeUsedWithSurfaceControl, browserInputToken);
}
@CalledByNative
private static SurfaceWrapper create(Surface surface, boolean canBeUsedWithSurfaceControl) {
return new SurfaceWrapper(surface, canBeUsedWithSurfaceControl);
@ -93,14 +133,28 @@ public class SurfaceWrapper implements Parcelable {
return new SurfaceWrapper(surfaceControl);
}
@SuppressLint("NewApi")
private static InputTransferToken createInputTokenFromParcel(Parcel in) {
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
InputTransferToken token = InputTransferToken.CREATOR.createFromParcel(in);
assert token != null;
return token;
}
public static final Parcelable.Creator<SurfaceWrapper> CREATOR =
new Parcelable.Creator<SurfaceWrapper>() {
@Override
public SurfaceWrapper createFromParcel(Parcel in) {
final boolean wrapsSurface = (in.readInt() == 1);
if (wrapsSurface) {
final boolean hasBrowserInputToken = (in.readInt() == 1);
Surface surface = Surface.CREATOR.createFromParcel(in);
boolean canBeUsedWithSurfaceControl = (in.readInt() == 1);
if (hasBrowserInputToken) {
InputTransferToken browserInputToken = createInputTokenFromParcel(in);
return new SurfaceWrapper(
surface, canBeUsedWithSurfaceControl, browserInputToken);
}
return new SurfaceWrapper(surface, canBeUsedWithSurfaceControl);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
SurfaceControl surfaceControl = SurfaceControl.CREATOR.createFromParcel(in);

@ -34,6 +34,8 @@ public class ContentFeatureList {
public static final String MOUSE_AND_TRACKPAD_DROPDOWN_MENU = "MouseAndTrackpadDropdownMenu";
public static final String INPUT_ON_VIZ = "InputOnViz";
public static final String OPTIMIZE_IMM_HIDE_CALLS = "OptimizeImmHideCalls";
public static final String ONE_TIME_PERMISSION = "OneTimePermission";

@ -0,0 +1,17 @@
// 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.
package org.chromium.content_public.common;
import android.os.Build;
import org.chromium.content_public.browser.ContentFeatureList;
import org.chromium.content_public.browser.ContentFeatureMap;
public class InputUtils {
public static boolean isTransferInputToVizSupported() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
&& ContentFeatureMap.isEnabled(ContentFeatureList.INPUT_ON_VIZ);
}
}

@ -74,8 +74,10 @@ class CONTENT_EXPORT Compositor {
virtual const gfx::Size& GetWindowBounds() = 0;
// Set the output surface which the compositor renders into.
virtual void SetSurface(const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control) = 0;
virtual void SetSurface(
const base::android::JavaRef<jobject>& surface,
bool can_be_used_with_surface_control,
const base::android::JavaRef<jobject>& host_input_token) = 0;
// Set the background color used by the layer tree host.
virtual void SetBackgroundColor(int color) = 0;

@ -172,6 +172,7 @@ source_set("ipc_common_sources") {
"gpu_surface_tracker.h",
]
libs = [ "android" ]
deps += [ "//ui/gl:surface_jni_headers" ]
}
if (use_ozone) {

@ -4,18 +4,34 @@
#include "gpu/ipc/common/gpu_surface_lookup.h"
#include <jni.h>
#include "base/check.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "ui/gl/surface_jni_headers/InputTransferToken_jni.h"
namespace gpu {
namespace {
GpuSurfaceLookup* g_instance = nullptr;
} // anonymous namespace
SurfaceRecord::SurfaceRecord(gl::ScopedJavaSurface surface,
bool can_be_used_with_surface_control)
SurfaceRecord::SurfaceRecord(
gl::ScopedJavaSurface surface,
bool can_be_used_with_surface_control,
const base::android::JavaRef<jobject>& host_input_token)
: surface_variant(std::move(surface)),
can_be_used_with_surface_control(can_be_used_with_surface_control) {}
can_be_used_with_surface_control(can_be_used_with_surface_control),
host_input_token(host_input_token) {
#if DCHECK_IS_ON()
JNIEnv* env = jni_zero::AttachCurrentThread();
if (host_input_token) {
DCHECK(env->IsInstanceOf(host_input_token.obj(),
android_window_InputTransferToken_clazz(env)));
}
#endif // DCHECK_IS_ON()
}
SurfaceRecord::SurfaceRecord(gl::ScopedJavaSurfaceControl surface_control)
: surface_variant(std::move(surface_control)),

@ -5,6 +5,7 @@
#ifndef GPU_IPC_COMMON_GPU_SURFACE_LOOKUP_H_
#define GPU_IPC_COMMON_GPU_SURFACE_LOOKUP_H_
#include "base/android/scoped_java_ref.h"
#include "gpu/gpu_export.h"
#include "gpu/ipc/common/surface_handle.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
@ -18,8 +19,10 @@ using JavaSurfaceVariant =
absl::variant<gl::ScopedJavaSurface, gl::ScopedJavaSurfaceControl>;
struct GPU_EXPORT SurfaceRecord {
SurfaceRecord(gl::ScopedJavaSurface surface,
bool can_be_used_with_surface_control);
SurfaceRecord(
gl::ScopedJavaSurface surface,
bool can_be_used_with_surface_control,
const base::android::JavaRef<jobject>& host_input_token = nullptr);
explicit SurfaceRecord(gl::ScopedJavaSurfaceControl surface_control);
~SurfaceRecord();
@ -28,6 +31,9 @@ struct GPU_EXPORT SurfaceRecord {
JavaSurfaceVariant surface_variant;
bool can_be_used_with_surface_control = false;
// Host input transfer token gotten from Android Window's root surface
// control.
base::android::ScopedJavaGlobalRef<jobject> host_input_token;
};
// This class provides an interface to look up window surface handles
// that cannot be sent through the IPC channel.

@ -59,7 +59,8 @@ SurfaceRecord GpuSurfaceTracker::AcquireJavaSurface(
[&](const gl::ScopedJavaSurface& surface) {
DCHECK(surface.IsValid());
return SurfaceRecord(surface.CopyRetainOwnership(),
it->second.can_be_used_with_surface_control);
it->second.can_be_used_with_surface_control,
it->second.host_input_token);
},
[&](const gl::ScopedJavaSurfaceControl& surface_control) {
return SurfaceRecord(surface_control.CopyRetainOwnership());

@ -9,7 +9,6 @@
#include <map>
#include "base/android/scoped_java_ref.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
#include "gpu/gpu_export.h"

@ -772,6 +772,15 @@ chromium-metrics-reviews@google.com.
<int value="3" label="Both 32-bit and 64-bit"/>
</enum>
<enum name="CreateAndroidInputReceiverResult">
<int value="0" label="SuccessfullyCreated"/>
<int value="1" label="FailedUnknown"/>
<int value="2" label="FailedNullSurfaceControl"/>
<int value="3" label="FailedNullLooper"/>
<int value="4" label="FailedNullInputTransferToken"/>
<int value="5" label="FailedNullCallbacks"/>
</enum>
<enum name="DebuggingEnabled">
<int value="0" label="Debugging not enabled."/>
<int value="1"

@ -2038,6 +2038,18 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="Android.InputOnViz.InputReceiverCreationResult"
enum="CreateAndroidInputReceiverResult" expires_after="2025-06-30">
<owner>kartarsingh@google.com</owner>
<owner>woa-performance-team@google.com</owner>
<summary>
Records result of InputReceiver creation on Viz. When Android returns null
pointer to some of the API calls made, input receiver creation API call
cannot be issued. This histogram tracks if input receiver creation was
successful or failed due to some reason.
</summary>
</histogram>
<histogram name="Android.Intent.BlockedExternalNavLastGestureTime" units="ms"
expires_after="2024-12-08">
<owner>mthiesse@chromium.org</owner>

@ -565,7 +565,10 @@ group("gl_unittests_ozone") {
if (is_android) {
generate_jar_jni("surface_jni_headers") {
classes = [ "android/view/Surface.class" ]
classes = [
"android/view/Surface.class",
"android/window/InputTransferToken.class",
]
}
generate_jni("gl_jni_headers") {