blob: 3e0c9072bddd5e3243e43c73f299c13b3558b115 [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.
#ifndef CHROME_BROWSER_VR_METRICS_SESSION_METRICS_HELPER_H_
#define CHROME_BROWSER_VR_METRICS_SESSION_METRICS_HELPER_H_
#include <memory>
#include "base/time/time.h"
#include "chrome/browser/vr/mode.h"
#include "chrome/browser/vr/ui_browser_interface.h"
#include "chrome/browser/vr/vr_export.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "url/gurl.h"
namespace vr {
// This enum describes various ways a Chrome VR session started.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Ensure that this stays in sync with VRSessionStartAction in enums.xml
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.vr
enum class VrStartAction : int {
// The user activated a headset. For example, inserted phone in Daydream, or
// put on an Occulus or Vive.
kHeadsetActivation = 1,
// The user triggered a presentation request on a page, probably by clicking
// an enter VR button.
kPresentationRequest = 2,
// The user launched a deep linked app, probably from Daydream home.
kDeepLinkedApp = 3,
// Chrome VR was started by an intent from another app. Most likely the user
// clicked the icon in Daydream home.
kIntentLaunch = 4,
kMaxValue = kIntentLaunch,
};
// The source of a request to enter XR Presentation.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Ensure that this stays in sync with VRPresentationStartAction in enums.xml.
enum PresentationStartAction {
// A catch all for methods of Presentation entry that are not otherwise
// logged.
kOther = 0,
// The user triggered a presentation request on a page in 2D, probably by
// clicking an enter VR button.
kRequestFrom2dBrowsing = 1,
// The user triggered a presentation request on a page in VR browsing,
// probably by clicking an enter VR button.
kRequestFromVrBrowsing = 2,
// The user activated a headset on a page that listens for headset activations
// and requests presentation.
kHeadsetActivation = 3,
// The user opened a deep linked app, probably from the Daydream homescreen.
kDeepLinkedApp = 4,
kMaxValue = kDeepLinkedApp,
};
// SessionTimer will monitor the time between calls to StartSession and
// StopSession. It will combine multiple segments into a single session if they
// are sufficiently close in time. It will also only include segments if they
// are sufficiently long.
// Because the session may be extended, the accumulated time is occasionally
// sent on destruction or when a new session begins.
class SessionTimer {
public:
virtual ~SessionTimer() {}
void StartSession(base::Time start_time);
void StopSession(bool continuable, base::Time stop_time);
protected:
SessionTimer() {}
virtual void SendAccumulatedSessionTime() = 0;
base::Time start_time_;
base::Time stop_time_;
base::TimeDelta accumulated_time_;
// Config members.
// Maximum time gap allowed between a StopSession and a StartSession before it
// will be logged as a seperate session.
base::TimeDelta maximum_session_gap_time_;
// Minimum time between a StartSession and StopSession required before it is
// added to the duration.
base::TimeDelta minimum_duration_;
DISALLOW_COPY_AND_ASSIGN(SessionTimer);
};
// SessionTracker tracks UKM data for sessions and sends the data upon request.
template <class T>
class SessionTracker {
public:
explicit SessionTracker(std::unique_ptr<T> entry)
: ukm_entry_(std::move(entry)),
start_time_(base::Time::Now()),
stop_time_(base::Time::Now()) {}
virtual ~SessionTracker() {}
T* ukm_entry() { return ukm_entry_.get(); }
void SetSessionEnd(base::Time stop_time) { stop_time_ = stop_time; }
int GetRoundedDurationInSeconds() {
if (start_time_ > stop_time_) {
// Return negative one to indicate an invalid value was recorded.
return -1;
}
base::TimeDelta duration = stop_time_ - start_time_;
if (duration.InHours() > 1) {
return duration.InHours() * 3600;
} else if (duration.InMinutes() > 10) {
return (duration.InMinutes() / 10) * 10 * 60;
} else if (duration.InSeconds() > 60) {
return duration.InMinutes() * 60;
} else {
return duration.InSeconds();
}
}
void RecordEntry() {
ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
DCHECK(ukm_recorder);
ukm_entry_->Record(ukm_recorder);
}
protected:
std::unique_ptr<T> ukm_entry_;
base::Time start_time_;
base::Time stop_time_;
DISALLOW_COPY_AND_ASSIGN(SessionTracker);
};
// This class is not thread-safe and must only be used from the main thread.
// This class tracks metrics for various kinds of sessions, including VR
// browsing sessions, WebXR presentation sessions, and others. It mainly tracks
// metrics that require state monitoring, such as durations, but also tracks
// data we want attached to that, such as number of videos watched and how the
// session was started.
class VR_EXPORT SessionMetricsHelper : public content::WebContentsObserver {
public:
// Returns the SessionMetricsHelper singleton if it has been created for the
// WebContents.
static SessionMetricsHelper* FromWebContents(content::WebContents* contents);
static SessionMetricsHelper* CreateForWebContents(
content::WebContents* contents,
Mode initial_mode,
bool started_with_autopresentation);
~SessionMetricsHelper() override;
void SetWebVREnabled(bool is_webvr_presenting);
void SetVRActive(bool is_vr_enabled);
void RecordVoiceSearchStarted();
void RecordUrlRequested(GURL url, NavigationMethod method);
void RecordVrStartAction(VrStartAction action);
void RecordPresentationStartAction(PresentationStartAction action);
void ReportRequestPresent();
private:
SessionMetricsHelper(content::WebContents* contents,
Mode initial_mode,
bool started_with_autopresentation);
// WebContentObserver
void MediaStartedPlaying(const MediaPlayerInfo& media_info,
const MediaPlayerId&) override;
void MediaStoppedPlaying(
const MediaPlayerInfo& media_info,
const MediaPlayerId&,
WebContentsObserver::MediaStoppedReason reason) override;
void DidStartNavigation(content::NavigationHandle* handle) override;
void DidFinishNavigation(content::NavigationHandle* handle) override;
void DidToggleFullscreenModeForTab(bool entered_fullscreen,
bool will_cause_resize) override;
void SetVrMode(Mode mode);
void UpdateMode();
void LogVrStartAction(VrStartAction action);
void LogPresentationStartAction(PresentationStartAction action);
void OnEnterAnyVr();
void OnExitAllVr();
void OnEnterRegularBrowsing();
void OnEnterPresentation();
void OnExitPresentation();
void OnEnterFullscreenBrowsing();
std::unique_ptr<SessionTimer> mode_video_timer_;
std::unique_ptr<SessionTimer> session_video_timer_;
std::unique_ptr<SessionTimer> mode_timer_;
std::unique_ptr<SessionTimer> session_timer_;
std::unique_ptr<SessionTracker<ukm::builders::XR_PageSession>>
page_session_tracker_;
std::unique_ptr<SessionTracker<ukm::builders::XR_WebXR_PresentationSession>>
presentation_session_tracker_;
Mode mode_ = Mode::kNoVr;
// State that gets translated into the VR mode.
bool is_fullscreen_ = false;
bool is_webvr_ = false;
bool is_vr_enabled_ = false;
bool started_with_autopresentation_ = false;
GURL last_requested_url_;
NavigationMethod last_url_request_method_;
base::Optional<VrStartAction> pending_page_session_start_action_;
base::Optional<PresentationStartAction> pending_presentation_start_action_;
int num_videos_playing_ = 0;
int num_session_navigation_ = 0;
int num_session_video_playback_ = 0;
int num_voice_search_started_ = 0;
GURL origin_;
};
} // namespace vr
#endif // CHROME_BROWSER_VR_METRICS_SESSION_METRICS_HELPER_H_