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:

committed by
Chromium LUCI CQ

parent
1ff4205484
commit
a7656a6b30
base/android
android_input_receiver_compat.ccandroid_input_receiver_compat.handroid_input_receiver_compat_unittest.cc
chrome
android
java
src
org
chromium
chrome
browser
compositor
browser
android
compositor
components
embedder_support
android
java
src
org
chromium
components
embedder_support
view
input
BUILD.gn
android
scoped_input_receiver.ccscoped_input_receiver.hscoped_input_receiver_callbacks.ccscoped_input_receiver_callbacks.hscoped_input_transfer_token.ccscoped_input_transfer_token.h
test
thin_webview
internal
viz
service
content
gpu/ipc/common
tools/metrics/histograms/metadata/android
ui/gl
@ -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",
|
||||
|
52
components/input/android/scoped_input_receiver.cc
Normal file
52
components/input/android/scoped_input_receiver.cc
Normal file
@ -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
|
54
components/input/android/scoped_input_receiver.h
Normal file
54
components/input/android/scoped_input_receiver.h
Normal file
@ -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_
|
47
components/input/android/scoped_input_receiver_callbacks.cc
Normal file
47
components/input/android/scoped_input_receiver_callbacks.cc
Normal file
@ -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
|
48
components/input/android/scoped_input_receiver_callbacks.h
Normal file
48
components/input/android/scoped_input_receiver_callbacks.h
Normal file
@ -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_
|
50
components/input/android/scoped_input_transfer_token.cc
Normal file
50
components/input/android/scoped_input_transfer_token.cc
Normal file
@ -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
|
50
components/input/android/scoped_input_transfer_token.h
Normal file
50
components/input/android/scoped_input_transfer_token.h
Normal file
@ -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") {
|
||||
|
Reference in New Issue
Block a user