blob: e3731fb29f77e0eec1a6a38085ad681396e49efb [file] [log] [blame]
# Copyright 2017 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.
import os
from core import path_util
path_util.AddAndroidPylibToPath()
from pylib.utils import shared_preference_utils
from telemetry.core import android_platform
from telemetry.core import util
from telemetry.page import shared_page_state
from contrib.vr_benchmarks.desktop_runtimes import oculus_runtimes
from contrib.vr_benchmarks.desktop_runtimes import openvr_runtimes
from contrib.vr_benchmarks.desktop_runtimes import wmr_runtimes
CARDBOARD_PATH = os.path.join('chrome', 'android', 'shared_preference_files',
'test', 'vr_cardboard_skipdon_setupcomplete.json')
class SharedVrPageStateFactory(shared_page_state.SharedPageState):
""""Factory" for picking the correct SharedVrPageState subclass.
This is a hacky way to automatically change the shared page state that's used
depending on which platform the benchmark is being run on. The
shared_page_state_class that gets passed to the Page constructor must be an
instance of SharedState, so we can't just pass a function pointer that returns
an instance of the correct subclass when called.
Additionally, we only really know what platform we're being run on after
SharedPageState's constructor is called, as we can't rely on the given
possible_browser to determine it.
So, we have to call SharedPageState's constructor, find out which platform
we're being run on, switch our class out for the correct one, and
re-construct ourselves.
"""
def __init__(self, test, finder_options, story_set, possible_browser=None):
super(SharedVrPageStateFactory, self).__init__(
test, finder_options, story_set, possible_browser)
if isinstance(self.platform, android_platform.AndroidPlatform):
self.__class__ = AndroidSharedVrPageState
elif self.platform.GetOSName().lower() == 'win':
self.__class__ = WindowsSharedVrPageState
else:
raise NotImplementedError(
'No VR SharedPageState implemented for platform %s' %
self.platform.GetOSName())
# Use self._possible_browser to avoid duplicate computation if
# possible_browser is None.
self.__init__(test, finder_options, story_set, self._possible_browser)
class _SharedVrPageState(shared_page_state.SharedPageState):
"""Abstract, platform-independent SharedPageState for VR tests.
Must be subclassed for each platform, since VR setup and tear down differs
between each.
"""
def __init__(self, test, finder_options, story_set, possible_browser=None):
super(_SharedVrPageState, self).__init__(
test, finder_options, story_set, possible_browser)
self._story_set = story_set
@property
def recording_wpr(self):
return self._finder_options.recording_wpr
def ShouldNavigateToBlankPageBeforeFinishing(self):
# TODO(https://crbug.com/941715): Always navigate once the issue with
# tracing metadata for the XR device process not being present when
# navigation occurs is fixed.
return False
class AndroidSharedVrPageState(_SharedVrPageState):
"""Android-specific VR SharedPageState.
Platform-specific functionality:
1. Performs Android VR-specific setup such as installing and configuring
additional APKs that are necessary for testing.
2. Cycles the screen off then on before each story, similar to how
AndroidScreenRestorationSharedState ensures that the screen is on. See
_CycleScreen() for an explanation on the reasoning behind this.
"""
def __init__(self, test, finder_options, story_set, possible_browser=None):
super(AndroidSharedVrPageState, self).__init__(
test, finder_options, story_set, possible_browser)
if not self._finder_options.disable_vrcore_install:
self._InstallVrCore()
self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(),
self._finder_options.shared_prefs_file))
self._InstallNfcApk()
if not self._finder_options.disable_keyboard_install:
self._InstallKeyboardApk()
def _InstallVrCore(self):
"""Installs the VrCore APK."""
self.platform.InstallApplication(
os.path.join(path_util.GetChromiumSrcDir(), 'third_party',
'gvr-android-sdk', 'test-apks', 'vr_services',
'vr_services_current.apk'))
def _ConfigureVrCore(self, filepath):
"""Configures VrCore using the provided settings file."""
settings = shared_preference_utils.ExtractSettingsFromJson(filepath)
for setting in settings:
shared_pref = self.platform.GetSharedPrefs(
setting['package'], setting['filename'],
use_encrypted_path=setting.get('supports_encrypted_path', False))
shared_preference_utils.ApplySharedPreferenceSetting(
shared_pref, setting)
def _InstallNfcApk(self):
"""Installs the APK that allows VR tests to simulate a headset NFC scan."""
chromium_root = path_util.GetChromiumSrcDir()
# Find the most recently build APK
candidate_apks = []
for build_path in util.GetBuildDirectories(chromium_root):
apk_path = os.path.join(build_path, 'apks', 'VrNfcSimulator.apk')
if os.path.exists(apk_path):
last_changed = os.path.getmtime(apk_path)
candidate_apks.append((last_changed, apk_path))
if not candidate_apks:
raise RuntimeError(
'Could not find VrNfcSimulator.apk in a build output directory')
newest_apk_path = sorted(candidate_apks)[-1][1]
self.platform.InstallApplication(
os.path.join(chromium_root, newest_apk_path))
def _InstallKeyboardApk(self):
"""Installs the VR Keyboard APK."""
self.platform.InstallApplication(
os.path.join(path_util.GetChromiumSrcDir(), 'third_party',
'gvr-android-sdk', 'test-apks', 'vr_keyboard',
'vr_keyboard_current.apk'))
def WillRunStory(self, page):
super(AndroidSharedVrPageState, self).WillRunStory(page)
if not self._finder_options.disable_screen_reset:
self._CycleScreen()
def TearDownState(self):
super(AndroidSharedVrPageState, self).TearDownState()
# Re-apply Cardboard as the viewer to leave the device in a consistent
# state after a benchmark run
# TODO(bsheedy): Remove this after crbug.com/772969 is fixed
self._ConfigureVrCore(os.path.join(path_util.GetChromiumSrcDir(),
CARDBOARD_PATH))
def _CycleScreen(self):
"""Cycles the screen off then on.
This is because VR test devices are set to have normal screen brightness and
automatically turn off after several minutes instead of the usual approach
of having the screen always on at minimum brightness. This is due to the
motion-to-photon latency test being sensitive to screen brightness, and min
brightness does not work well for it.
Simply using TurnScreenOn does not actually reset the timer for turning off
the screen, so instead cycle the screen to refresh it periodically.
"""
self.platform.android_action_runner.TurnScreenOff()
self.platform.android_action_runner.TurnScreenOn()
def ShouldNavigateToBlankPageBeforeFinishing(self):
# Android devices generate a lot of heat while in VR, so navigate away from
# the VR page after we're done collecting data so that we aren't in VR while
# metric calculation is occurring.
return True
class WindowsSharedVrPageState(_SharedVrPageState):
"""Windows-specific VR SharedPageState.
Platform-specific functionality involves starting and stopping different
VR runtimes before and after all stories are run.
"""
# Constants to make the below map more readable
MOCK_RUNTIME = False
REAL_RUNTIME = True
# Map of runtime names to runtime classes for both real and mock
# implementations. Real runtimes require specialized hardware and software
# to be installed, i.e. exactly how a real user would use VR. Mock runtimes
# avoid this, but can't necessarily be implemented.
DESKTOP_RUNTIMES = {
'oculus': {
MOCK_RUNTIME: oculus_runtimes.OculusRuntimeMock,
REAL_RUNTIME: oculus_runtimes.OculusRuntimeReal,
},
'openvr': {
MOCK_RUNTIME: openvr_runtimes.OpenVRRuntimeMock,
REAL_RUNTIME: openvr_runtimes.OpenVRRuntimeReal
},
'wmr': {
MOCK_RUNTIME: wmr_runtimes.WMRRuntimeMock,
REAL_RUNTIME: wmr_runtimes.WMRRuntimeReal,
},
}
def __init__(self, test, finder_options, story_set, possible_browser):
super(WindowsSharedVrPageState, self).__init__(
test, finder_options, story_set, possible_browser)
# Get the specific runtime implementation depending on runtime choice and
# whether we're using the real or mock one.
self._desktop_runtime = self.DESKTOP_RUNTIMES[
self._finder_options.desktop_runtime][
self._finder_options.use_real_runtime](
self._finder_options, self._possible_browser)
self._desktop_runtime.Setup()
def WillRunStory(self, page):
super(WindowsSharedVrPageState, self).WillRunStory(page)
self._desktop_runtime.WillRunStory()
def TearDownState(self):
super(WindowsSharedVrPageState, self).TearDownState()
self._desktop_runtime.TearDown()