Commit 0275d360 authored by Tibor Goldschwendt's avatar Tibor Goldschwendt Committed by Commit Bot

[vr] On-demand install VR dynamic feature module

With this CL we initate the module install once the user enters VR on a
WebXR/VR page. In more detail this CL

- Introduces native VrModuleDelegate used to request VR module from
  native,

- Refactors GvrDevice to request the VR module as part of its
  initialization flow,

- Adds native side of the VrModuleProvider for Java-native
  communication.

Bug: 862689
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I960d2bdd4d2c64a19fa6069e457d44f77ab0e73b
Reviewed-on: https://chromium-review.googlesource.com/c/1241458
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602922}
parent d5ff4f11
...@@ -1424,6 +1424,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent> ...@@ -1424,6 +1424,7 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
maybeRemoveWindowBackground(); maybeRemoveWindowBackground();
DownloadManagerService.getDownloadManagerService().onActivityLaunched(); DownloadManagerService.getDownloadManagerService().onActivityLaunched();
VrModuleProvider.init();
VrModuleProvider.getDelegate().onNativeLibraryAvailable(); VrModuleProvider.getDelegate().onNativeLibraryAvailable();
if (getSavedInstanceState() == null && getIntent() != null) { if (getSavedInstanceState() == null && getIntent() != null) {
VrModuleProvider.getDelegate().onNewIntentWithNative(this, getIntent()); VrModuleProvider.getDelegate().onNewIntentWithNative(this, getIntent());
......
...@@ -16,8 +16,7 @@ import org.chromium.chrome.R; ...@@ -16,8 +16,7 @@ import org.chromium.chrome.R;
R.string.vr_services_check_infobar_update_text, R.string.vr_services_check_infobar_update_text,
R.string.vr_services_check_infobar_install_button, R.string.vr_services_check_infobar_install_button,
R.string.vr_services_check_infobar_update_button, R.anim.stay_hidden, R.string.vr_services_check_infobar_update_button, R.anim.stay_hidden,
R.drawable.vr_services, R.string.vr_module_title, R.string.vr_module_install_start_text, R.drawable.vr_services, R.string.vr_module_title,
R.string.vr_module_install_success_text, R.string.vr_module_install_failure_text,
}; };
private SilenceLintErrors() {} private SilenceLintErrors() {}
......
...@@ -4,7 +4,12 @@ ...@@ -4,7 +4,12 @@
package org.chromium.chrome.browser.vr; package org.chromium.chrome.browser.vr;
import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.R;
import org.chromium.components.module_installer.ModuleInstaller;
import org.chromium.ui.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -18,6 +23,13 @@ public class VrModuleProvider { ...@@ -18,6 +23,13 @@ public class VrModuleProvider {
private static VrDelegateProvider sDelegateProvider; private static VrDelegateProvider sDelegateProvider;
private static final List<VrModeObserver> sVrModeObservers = new ArrayList<>(); private static final List<VrModeObserver> sVrModeObservers = new ArrayList<>();
private long mNativeVrModuleProvider;
/** Need to be called after native libraries are available. */
public static void init() {
nativeInit();
}
public static VrDelegate getDelegate() { public static VrDelegate getDelegate() {
return getDelegateProvider().getDelegate(); return getDelegateProvider().getDelegate();
} }
...@@ -60,6 +72,8 @@ public class VrModuleProvider { ...@@ -60,6 +72,8 @@ public class VrModuleProvider {
private static VrDelegateProvider getDelegateProvider() { private static VrDelegateProvider getDelegateProvider() {
if (sDelegateProvider == null) { if (sDelegateProvider == null) {
// Need to be called before trying to access the VR module.
ModuleInstaller.init();
try { try {
sDelegateProvider = sDelegateProvider =
(VrDelegateProvider) Class (VrDelegateProvider) Class
...@@ -73,7 +87,54 @@ public class VrModuleProvider { ...@@ -73,7 +87,54 @@ public class VrModuleProvider {
return sDelegateProvider; return sDelegateProvider;
} }
private VrModuleProvider() {} @CalledByNative
private static VrModuleProvider create(long nativeVrModuleProvider) {
return new VrModuleProvider(nativeVrModuleProvider);
}
@CalledByNative
private static boolean isModuleInstalled() {
return !(getDelegateProvider() instanceof VrDelegateProviderFallback);
}
private VrModuleProvider(long nativeVrModuleProvider) {
mNativeVrModuleProvider = nativeVrModuleProvider;
}
@CalledByNative
private void onNativeDestroy() {
mNativeVrModuleProvider = 0;
}
@CalledByNative
private void installModule() {
assert !isModuleInstalled();
// TODO(crbug.com/863064): This is a placeholder UI. Replace once proper UI is spec'd.
Toast.makeText(ContextUtils.getApplicationContext(), R.string.vr_module_install_start_text,
Toast.LENGTH_SHORT)
.show();
ModuleInstaller.install("vr", (success) -> {
if (success) {
// Re-create delegate provider.
sDelegateProvider = null;
VrDelegate delegate = getDelegate();
assert !(delegate instanceof VrDelegateFallback);
delegate.onNativeLibraryAvailable();
}
// TODO(crbug.com/863064): This is a placeholder UI. Replace once proper UI is spec'd.
int mToastTextRes = success ? R.string.vr_module_install_success_text
: R.string.vr_module_install_failure_text;
Toast.makeText(ContextUtils.getApplicationContext(), mToastTextRes, Toast.LENGTH_SHORT)
.show();
if (mNativeVrModuleProvider != 0) {
nativeOnInstalledModule(mNativeVrModuleProvider, success);
}
});
}
private static native void nativeInit();
private static native void nativeRegisterJni(); private static native void nativeRegisterJni();
private native void nativeOnInstalledModule(long nativeVrModuleProvider, boolean success);
} }
...@@ -7,15 +7,13 @@ ...@@ -7,15 +7,13 @@
xmlns:dist="http://schemas.android.com/apk/distribution" xmlns:dist="http://schemas.android.com/apk/distribution"
package="{{manifest_package}}"> package="{{manifest_package}}">
<!-- Chrome VR is only supported on Android L+. --> <!-- The VR module is only supported on Android L+. -->
<uses-sdk <uses-sdk
android:minSdkVersion="21" android:minSdkVersion="21"
android:targetSdkVersion="{{target_sdk_version}}" /> android:targetSdkVersion="{{target_sdk_version}}" />
<!-- TODO(crbug.com/862689): Set dist:onDemand="true" once we can on-demand
install modules. -->
<dist:module <dist:module
dist:onDemand="false" dist:onDemand="true"
dist:title="@string/vr_module_title"> dist:title="@string/vr_module_title">
<dist:fusing dist:include="false" /> <dist:fusing dist:include="false" />
</dist:module> </dist:module>
......
...@@ -51,6 +51,7 @@ static_library("vr_android") { ...@@ -51,6 +51,7 @@ static_library("vr_android") {
"vr_input_connection.cc", "vr_input_connection.cc",
"vr_input_connection.h", "vr_input_connection.h",
"vr_module_provider.cc", "vr_module_provider.cc",
"vr_module_provider.h",
"vr_shell.cc", "vr_shell.cc",
"vr_shell.h", "vr_shell.h",
"vr_shell_delegate.cc", "vr_shell_delegate.cc",
......
...@@ -2,11 +2,64 @@ ...@@ -2,11 +2,64 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/android/vr/vr_module_provider.h"
#include "chrome/browser/android/vr/register_jni.h" #include "chrome/browser/android/vr/register_jni.h"
#include "chrome/browser/android/vr/vr_module_provider.h"
#include "device/vr/android/gvr/vr_module_delegate.h"
#include "jni/VrModuleProvider_jni.h" #include "jni/VrModuleProvider_jni.h"
namespace vr { namespace vr {
VrModuleProvider::VrModuleProvider() = default;
VrModuleProvider::~VrModuleProvider() {
if (!j_vr_module_provider_.obj()) {
return;
}
Java_VrModuleProvider_onNativeDestroy(base::android::AttachCurrentThread(),
j_vr_module_provider_);
}
bool VrModuleProvider::ModuleInstalled() {
return Java_VrModuleProvider_isModuleInstalled(
base::android::AttachCurrentThread());
}
void VrModuleProvider::InstallModule(
base::OnceCallback<void(bool)> on_finished) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
on_finished_callbacks_.push(std::move(on_finished));
// Don't request VR module multiple times in parallel.
if (on_finished_callbacks_.size() > 1) {
return;
}
DCHECK(!j_vr_module_provider_.obj());
j_vr_module_provider_.Reset(Java_VrModuleProvider_create(
base::android::AttachCurrentThread(), (jlong) this));
Java_VrModuleProvider_installModule(base::android::AttachCurrentThread(),
j_vr_module_provider_);
}
void VrModuleProvider::OnInstalledModule(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(on_finished_callbacks_.size() > 0);
while (!on_finished_callbacks_.empty()) {
std::move(on_finished_callbacks_.front()).Run(success);
on_finished_callbacks_.pop();
}
j_vr_module_provider_ = nullptr;
}
static void JNI_VrModuleProvider_Init(
JNIEnv* env,
const base::android::JavaParamRef<jclass>& clazz) {
device::VrModuleDelegate::Set(std::make_unique<VrModuleProvider>());
}
static void JNI_VrModuleProvider_RegisterJni( static void JNI_VrModuleProvider_RegisterJni(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jclass>& clazz) { const base::android::JavaParamRef<jclass>& clazz) {
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ANDROID_VR_VR_MODULE_PROVIDER_H_
#define CHROME_BROWSER_ANDROID_VR_VR_MODULE_PROVIDER_H_
#include <jni.h>
#include <queue>
#include "base/android/jni_android.h"
#include "device/vr/android/gvr/vr_module_delegate.h"
namespace vr {
// Installs the VR module.
class VrModuleProvider : public device::VrModuleDelegate {
public:
VrModuleProvider();
~VrModuleProvider() override;
bool ModuleInstalled() override;
void InstallModule(base::OnceCallback<void(bool)> on_finished) override;
// Called by Java.
void OnInstalledModule(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
bool success);
private:
std::queue<base::OnceCallback<void(bool)>> on_finished_callbacks_;
base::android::ScopedJavaGlobalRef<jobject> j_vr_module_provider_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace vr
#endif // CHROME_BROWSER_ANDROID_VR_VR_MODULE_PROVIDER_H_
...@@ -63,6 +63,8 @@ if (enable_vr) { ...@@ -63,6 +63,8 @@ if (enable_vr) {
"android/gvr/gvr_gamepad_data_fetcher.cc", "android/gvr/gvr_gamepad_data_fetcher.cc",
"android/gvr/gvr_gamepad_data_fetcher.h", "android/gvr/gvr_gamepad_data_fetcher.h",
"android/gvr/gvr_gamepad_data_provider.h", "android/gvr/gvr_gamepad_data_provider.h",
"android/gvr/vr_module_delegate.cc",
"android/gvr/vr_module_delegate.h",
] ]
if (enable_arcore) { if (enable_arcore) {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "device/vr/android/gvr/gvr_delegate_provider.h" #include "device/vr/android/gvr/gvr_delegate_provider.h"
#include "device/vr/android/gvr/gvr_delegate_provider_factory.h" #include "device/vr/android/gvr/gvr_delegate_provider_factory.h"
#include "device/vr/android/gvr/gvr_device_provider.h" #include "device/vr/android/gvr/gvr_device_provider.h"
#include "device/vr/android/gvr/vr_module_delegate.h"
#include "device/vr/vr_display_impl.h" #include "device/vr/vr_display_impl.h"
#include "jni/NonPresentingGvrContext_jni.h" #include "jni/NonPresentingGvrContext_jni.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
...@@ -161,33 +162,12 @@ void GvrDevice::RequestSession( ...@@ -161,33 +162,12 @@ void GvrDevice::RequestSession(
mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) { mojom::XRRuntime::RequestSessionCallback callback) {
if (!gvr_api_) { if (!gvr_api_) {
EnsureGvrReady(); Init(base::BindOnce(&GvrDevice::OnInitRequestSessionFinished,
if (!gvr_api_) { base::Unretained(this), std::move(options),
std::move(callback).Run(nullptr, nullptr); std::move(callback)));
return;
}
}
if (!options->immersive) {
// TODO(https://crbug.com/695937): This should be NOTREACHED() once we no
// longer need the hacked GRV non-immersive mode. This should now only be
// hit if orientation devices are disabled by flag.
ReturnNonImmersiveSession(std::move(callback));
return; return;
} }
OnInitRequestSessionFinished(std::move(options), std::move(callback), true);
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider) {
std::move(callback).Run(nullptr, nullptr);
return;
}
// StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
// that pauses Chrome.
delegate_provider->StartWebXRPresentation(
GetVRDisplayInfo(), std::move(options),
base::BindOnce(&GvrDevice::OnStartPresentResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void GvrDevice::OnStartPresentResult( void GvrDevice::OnStartPresentResult(
...@@ -233,30 +213,6 @@ void GvrDevice::StopPresenting() { ...@@ -233,30 +213,6 @@ void GvrDevice::StopPresenting() {
exclusive_controller_binding_.Close(); exclusive_controller_binding_.Close();
} }
void GvrDevice::EnsureGvrReady() {
if (!non_presenting_context_.obj() || !gvr_api_) {
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider || delegate_provider->ShouldDisableGvrDevice())
return;
JNIEnv* env = base::android::AttachCurrentThread();
non_presenting_context_.Reset(Java_NonPresentingGvrContext_create(
env, reinterpret_cast<jlong>(this)));
if (!non_presenting_context_.obj())
return;
jlong context = Java_NonPresentingGvrContext_getNativeGvrContext(
env, non_presenting_context_);
gvr_api_ =
gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(context));
SetVRDisplayInfo(CreateVRDisplayInfo(gvr_api_.get(), GetId()));
if (paused_) {
PauseTracking();
} else {
ResumeTracking();
}
}
}
void GvrDevice::OnMagicWindowFrameDataRequest( void GvrDevice::OnMagicWindowFrameDataRequest(
mojom::XRFrameDataProvider::GetFrameDataCallback callback) { mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
if (!gvr_api_) { if (!gvr_api_) {
...@@ -295,8 +251,9 @@ void GvrDevice::ResumeTracking() { ...@@ -295,8 +251,9 @@ void GvrDevice::ResumeTracking() {
} }
void GvrDevice::EnsureInitialized(EnsureInitializedCallback callback) { void GvrDevice::EnsureInitialized(EnsureInitializedCallback callback) {
EnsureGvrReady(); Init(base::BindOnce([](EnsureInitializedCallback callback,
std::move(callback).Run(); bool success) { std::move(callback).Run(); },
std::move(callback)));
} }
GvrDelegateProvider* GvrDevice::GetGvrDelegateProvider() { GvrDelegateProvider* GvrDevice::GetGvrDelegateProvider() {
...@@ -319,4 +276,85 @@ void GvrDevice::Activate(mojom::VRDisplayEventReason reason, ...@@ -319,4 +276,85 @@ void GvrDevice::Activate(mojom::VRDisplayEventReason reason,
OnActivate(reason, std::move(on_handled)); OnActivate(reason, std::move(on_handled));
} }
void GvrDevice::Init(base::OnceCallback<void(bool)> on_finished) {
VrModuleDelegate* module_delegate = VrModuleDelegate::Get();
if (!module_delegate) {
std::move(on_finished).Run(false);
return;
}
if (!module_delegate->ModuleInstalled()) {
module_delegate->InstallModule(
base::BindOnce(&GvrDevice::OnVrModuleInstalled, base::Unretained(this),
std::move(on_finished)));
return;
}
OnVrModuleInstalled(std::move(on_finished), true);
}
void GvrDevice::OnVrModuleInstalled(base::OnceCallback<void(bool)> on_finished,
bool success) {
if (!success) {
std::move(on_finished).Run(false);
return;
}
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider || delegate_provider->ShouldDisableGvrDevice()) {
std::move(on_finished).Run(false);
return;
}
CreateNonPresentingContext();
std::move(on_finished).Run(non_presenting_context_.obj() != nullptr);
}
void GvrDevice::CreateNonPresentingContext() {
if (non_presenting_context_.obj())
return;
JNIEnv* env = base::android::AttachCurrentThread();
non_presenting_context_.Reset(
Java_NonPresentingGvrContext_create(env, reinterpret_cast<jlong>(this)));
if (!non_presenting_context_.obj())
return;
jlong context = Java_NonPresentingGvrContext_getNativeGvrContext(
env, non_presenting_context_);
gvr_api_ = gvr::GvrApi::WrapNonOwned(reinterpret_cast<gvr_context*>(context));
SetVRDisplayInfo(CreateVRDisplayInfo(gvr_api_.get(), GetId()));
if (paused_) {
PauseTracking();
} else {
ResumeTracking();
}
}
void GvrDevice::OnInitRequestSessionFinished(
mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback,
bool success) {
if (!success) {
std::move(callback).Run(nullptr, nullptr);
return;
}
if (!options->immersive) {
// TODO(https://crbug.com/695937): This should be NOTREACHED() once we no
// longer need the hacked GRV non-immersive mode. This should now only be
// hit if orientation devices are disabled by flag.
ReturnNonImmersiveSession(std::move(callback));
return;
}
GvrDelegateProvider* delegate_provider = GetGvrDelegateProvider();
if (!delegate_provider) {
std::move(callback).Run(nullptr, nullptr);
return;
}
// StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
// that pauses Chrome.
delegate_provider->StartWebXRPresentation(
GetVRDisplayInfo(), std::move(options),
base::BindOnce(&GvrDevice::OnStartPresentResult,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
} // namespace device } // namespace device
...@@ -53,9 +53,17 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase, ...@@ -53,9 +53,17 @@ class DEVICE_VR_EXPORT GvrDevice : public VRDeviceBase,
void OnPresentingControllerMojoConnectionError(); void OnPresentingControllerMojoConnectionError();
void StopPresenting(); void StopPresenting();
void EnsureGvrReady();
GvrDelegateProvider* GetGvrDelegateProvider(); GvrDelegateProvider* GetGvrDelegateProvider();
void Init(base::OnceCallback<void(bool)> on_finished);
void OnVrModuleInstalled(base::OnceCallback<void(bool)> on_finished,
bool success);
void CreateNonPresentingContext();
void OnInitRequestSessionFinished(
mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback,
bool success);
base::android::ScopedJavaGlobalRef<jobject> non_presenting_context_; base::android::ScopedJavaGlobalRef<jobject> non_presenting_context_;
std::unique_ptr<gvr::GvrApi> gvr_api_; std::unique_ptr<gvr::GvrApi> gvr_api_;
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/android/gvr/vr_module_delegate.h"
namespace device {
namespace {
// Storing the global delegate in a raw pointer - as opposed to e.g. an
// std::unique_ptr - to avoid adding a static initializer.
VrModuleDelegate* g_vr_module_delegate = nullptr;
} // namespace
// static
VrModuleDelegate* VrModuleDelegate::Get() {
return g_vr_module_delegate;
}
// static
void VrModuleDelegate::Set(std::unique_ptr<VrModuleDelegate> delegate) {
if (g_vr_module_delegate) {
delete g_vr_module_delegate;
}
g_vr_module_delegate = delegate.release();
}
} // namespace device
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_
#define DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_
#include "base/callback.h"
#include "base/macros.h"
#include "device/vr/vr_export.h"
namespace device {
// Delegates installation of the VR module.
class DEVICE_VR_EXPORT VrModuleDelegate {
public:
// Returns the global module delegate.
static VrModuleDelegate* Get();
// Sets the global module delegate.
static void Set(std::unique_ptr<VrModuleDelegate> delegate);
VrModuleDelegate() = default;
virtual ~VrModuleDelegate() = default;
// Returns true if the VR module is installed.
virtual bool ModuleInstalled() = 0;
// Asynchronously requests to install the VR module. |on_finished| is called
// after the module install is completed. If |success| is false the module
// install failed.
virtual void InstallModule(
base::OnceCallback<void(bool success)> on_finished) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(VrModuleDelegate);
};
} // namespace device
#endif // DEVICE_VR_ANDROID_GVR_VR_MODULE_DELEGATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment