blob: ead33c87dab6952a075183d8fbd37a41712b0925 [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/android/vr/vr_shell_delegate.h"
#include <utility>
#include "base/android/jni_android.h"
#include "base/callback_helpers.h"
#include "chrome/browser/android/vr/arcore_device/arcore_device_provider.h"
#include "chrome/browser/android/vr/metrics_util_android.h"
#include "chrome/browser/android/vr/vr_shell.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/vr_assets_component_installer.h"
#include "chrome/browser/vr/assets_loader.h"
#include "chrome/browser/vr/metrics/metrics_helper.h"
#include "chrome/browser/vr/service/browser_xr_runtime.h"
#include "chrome/browser/vr/service/vr_service_impl.h"
#include "chrome/browser/vr/service/xr_runtime_manager.h"
#include "content/public/browser/webvr_service_provider.h"
#include "device/vr/android/gvr/gvr_delegate_provider_factory.h"
#include "device/vr/android/gvr/gvr_device.h"
#include "device/vr/buildflags/buildflags.h"
#include "device/vr/public/mojom/vr_service.mojom.h"
#include "jni/VrShellDelegate_jni.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::AttachCurrentThread;
using base::android::ScopedJavaLocalRef;
namespace vr {
namespace {
class VrShellDelegateProviderFactory
: public device::GvrDelegateProviderFactory {
public:
VrShellDelegateProviderFactory() = default;
~VrShellDelegateProviderFactory() override = default;
device::GvrDelegateProvider* CreateGvrDelegateProvider() override;
private:
DISALLOW_COPY_AND_ASSIGN(VrShellDelegateProviderFactory);
};
device::GvrDelegateProvider*
VrShellDelegateProviderFactory::CreateGvrDelegateProvider() {
return VrShellDelegate::CreateVrShellDelegate();
}
} // namespace
VrShellDelegate::VrShellDelegate(JNIEnv* env, jobject obj)
: task_runner_(base::ThreadTaskRunnerHandle::Get()),
weak_ptr_factory_(this) {
DVLOG(1) << __FUNCTION__ << "=" << this;
j_vr_shell_delegate_.Reset(env, obj);
XRRuntimeManager::AddObserver(this);
}
VrShellDelegate::~VrShellDelegate() {
DVLOG(1) << __FUNCTION__ << "=" << this;
XRRuntimeManager::RemoveObserver(this);
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device)
gvr_device->OnExitPresent();
if (!on_present_result_callback_.is_null())
base::ResetAndReturn(&on_present_result_callback_).Run(false);
}
device::GvrDelegateProvider* VrShellDelegate::CreateVrShellDelegate() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> jdelegate = Java_VrShellDelegate_getInstance(env);
if (!jdelegate.is_null())
return GetNativeVrShellDelegate(env, jdelegate);
return nullptr;
}
VrShellDelegate* VrShellDelegate::GetNativeVrShellDelegate(
JNIEnv* env,
const JavaRef<jobject>& jdelegate) {
return reinterpret_cast<VrShellDelegate*>(
Java_VrShellDelegate_getNativePointer(env, jdelegate));
}
void VrShellDelegate::SetDelegate(VrShell* vr_shell,
gvr::ViewerType viewer_type) {
vr_shell_ = vr_shell;
// When VrShell is created, we disable magic window mode as the user is inside
// the headset. As currently implemented, orientation-based magic window
// doesn't make sense when the window is fixed and the user is moving.
SetInlineVrEnabled(false);
if (pending_successful_present_request_) {
CHECK(!on_present_result_callback_.is_null());
pending_successful_present_request_ = false;
base::ResetAndReturn(&on_present_result_callback_).Run(true);
}
if (pending_vr_start_action_) {
vr_shell_->RecordVrStartAction(*pending_vr_start_action_);
pending_vr_start_action_ = base::nullopt;
}
JNIEnv* env = AttachCurrentThread();
std::unique_ptr<VrCoreInfo> vr_core_info = MakeVrCoreInfo(env);
MetricsUtilAndroid::LogGvrVersionForVrViewerType(viewer_type, *vr_core_info);
}
void VrShellDelegate::RemoveDelegate() {
vr_shell_ = nullptr;
if (pending_successful_present_request_) {
CHECK(!on_present_result_callback_.is_null());
pending_successful_present_request_ = false;
base::ResetAndReturn(&on_present_result_callback_).Run(false);
}
SetInlineVrEnabled(true);
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device)
gvr_device->OnExitPresent();
}
void VrShellDelegate::SetPresentResult(JNIEnv* env,
const JavaParamRef<jobject>& obj,
jboolean success) {
CHECK(!on_present_result_callback_.is_null());
base::ResetAndReturn(&on_present_result_callback_)
.Run(static_cast<bool>(success));
}
void VrShellDelegate::RecordVrStartAction(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jint start_action) {
VrStartAction action = static_cast<VrStartAction>(start_action);
if (action == VrStartAction::kDeepLinkedApp) {
// If this is a deep linked app we expect a DisplayActivate to be coming
// down the pipeline shortly.
possible_presentation_start_action_ =
PresentationStartAction::kDeepLinkedApp;
}
if (!vr_shell_) {
pending_vr_start_action_ = action;
return;
}
vr_shell_->RecordVrStartAction(action);
}
void VrShellDelegate::OnPresentResult(
device::mojom::VRDisplayInfoPtr display_info,
device::mojom::XRRuntimeSessionOptionsPtr options,
base::OnceCallback<void(device::mojom::XRSessionPtr)> callback,
bool success) {
DVLOG(1) << __FUNCTION__ << ": success=" << success;
if (!success) {
std::move(callback).Run(nullptr);
possible_presentation_start_action_ = base::nullopt;
return;
}
if (!vr_shell_) {
// We have to wait until the GL thread is ready since we have to get the
// XRPresentationClient.
pending_successful_present_request_ = true;
on_present_result_callback_ = base::BindOnce(
&VrShellDelegate::OnPresentResult, base::Unretained(this),
std::move(display_info), std::move(options), std::move(callback));
return;
}
// If possible_presentation_start_action_ is not set at this point, then this
// request present probably came from blink, and has already been reported
// from there.
if (possible_presentation_start_action_) {
vr_shell_->RecordPresentationStartAction(
*possible_presentation_start_action_);
possible_presentation_start_action_ = base::nullopt;
}
DVLOG(1) << __FUNCTION__ << ": connecting presenting service";
request_present_response_callback_ = std::move(callback);
vr_shell_->ConnectPresentingService(std::move(display_info),
std::move(options));
}
void VrShellDelegate::SetInlineVrEnabled(bool enable) {
base::RepeatingCallback<void(BrowserXRRuntime*)> fn = base::BindRepeating(
[](bool flag, BrowserXRRuntime* runtime) {
runtime->GetRuntime()->SetInlinePosesEnabled(flag);
},
enable);
XRRuntimeManager::GetInstance()->ForEachRuntime(fn);
}
void VrShellDelegate::SendRequestPresentReply(
device::mojom::XRSessionPtr session) {
DVLOG(1) << __FUNCTION__;
if (!request_present_response_callback_) {
DLOG(ERROR) << __FUNCTION__ << ": ERROR: no callback";
return;
}
base::ResetAndReturn(&request_present_response_callback_)
.Run(std::move(session));
}
void VrShellDelegate::DisplayActivate(JNIEnv* env,
const JavaParamRef<jobject>& obj) {
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device) {
if (!possible_presentation_start_action_ ||
possible_presentation_start_action_ !=
PresentationStartAction::kDeepLinkedApp) {
// The only possible sources for DisplayActivate are at the moment DLAs
// and HeadsetActivations. Therefore if it's not a DLA it must be a
// HeadsetActivation.
possible_presentation_start_action_ =
PresentationStartAction::kHeadsetActivation;
}
gvr_device->Activate(
device::mojom::VRDisplayEventReason::MOUNTED,
base::BindRepeating(&VrShellDelegate::OnActivateDisplayHandled,
weak_ptr_factory_.GetWeakPtr()));
} else {
OnActivateDisplayHandled(true /* will_not_present */);
}
}
void VrShellDelegate::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) {
if (vr_shell_)
return;
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device)
gvr_device->PauseTracking();
}
void VrShellDelegate::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) {
if (vr_shell_)
return;
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device)
gvr_device->ResumeTracking();
}
void VrShellDelegate::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) {
delete this;
}
bool VrShellDelegate::ShouldDisableGvrDevice() {
int vr_support_level =
Java_VrShellDelegate_getVrSupportLevel(AttachCurrentThread());
return static_cast<VrSupportLevel>(vr_support_level) <=
VrSupportLevel::kVrNeedsUpdate;
}
void VrShellDelegate::StartWebXRPresentation(
device::mojom::VRDisplayInfoPtr display_info,
device::mojom::XRRuntimeSessionOptionsPtr options,
base::OnceCallback<void(device::mojom::XRSessionPtr)> callback) {
if (!on_present_result_callback_.is_null() ||
!request_present_response_callback_.is_null()) {
// Can only handle one request at a time. This is also extremely unlikely to
// happen in practice.
std::move(callback).Run(nullptr);
return;
}
on_present_result_callback_ = base::BindOnce(
&VrShellDelegate::OnPresentResult, base::Unretained(this),
std::move(display_info), std::move(options), std::move(callback));
// If/When VRShell is ready for use it will call SetPresentResult.
JNIEnv* env = AttachCurrentThread();
Java_VrShellDelegate_presentRequested(env, j_vr_shell_delegate_);
}
void VrShellDelegate::ExitWebVRPresent() {
JNIEnv* env = AttachCurrentThread();
Java_VrShellDelegate_exitWebVRPresent(env, j_vr_shell_delegate_);
device::GvrDevice* gvr_device = GetGvrDevice();
if (gvr_device)
gvr_device->OnExitPresent();
}
std::unique_ptr<VrCoreInfo> VrShellDelegate::MakeVrCoreInfo(JNIEnv* env) {
return std::unique_ptr<VrCoreInfo>(reinterpret_cast<VrCoreInfo*>(
Java_VrShellDelegate_getVrCoreInfo(env, j_vr_shell_delegate_)));
}
void VrShellDelegate::OnActivateDisplayHandled(bool will_not_present) {
if (will_not_present) {
// WebVR page didn't request presentation in the vrdisplayactivate handler.
// Tell VrShell that we are in VR Browsing Mode.
ExitWebVRPresent();
// Reset possible_presentation_start_action_ as it may have been set.
possible_presentation_start_action_ = base::nullopt;
}
}
void VrShellDelegate::OnListeningForActivateChanged(bool listening) {
JNIEnv* env = AttachCurrentThread();
Java_VrShellDelegate_setListeningForWebVrActivate(env, j_vr_shell_delegate_,
listening);
}
void VrShellDelegate::OnRuntimeAdded(vr::BrowserXRRuntime* runtime) {
if (vr_shell_) {
// See comment in VrShellDelegate::SetDelegate. This handles the case where
// VrShell is created before the device code is initialized (like when
// entering VR browsing on a non-webVR page).
runtime->GetRuntime()->SetInlinePosesEnabled(false);
}
}
device::GvrDevice* VrShellDelegate::GetGvrDevice() {
return device::GvrDelegateProviderFactory::GetDevice();
}
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
jlong JNI_VrShellDelegate_Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
return reinterpret_cast<intptr_t>(new VrShellDelegate(env, obj));
}
static void JNI_VrShellDelegate_OnLibraryAvailable(JNIEnv* env) {
device::GvrDelegateProviderFactory::Install(
std::make_unique<VrShellDelegateProviderFactory>());
}
static void JNI_VrShellDelegate_RegisterVrAssetsComponent(JNIEnv* env) {
component_updater::RegisterVrAssetsComponent(
g_browser_process->component_updater());
}
} // namespace vr