[iOS][BWG] Add Gemini session duration histograms by session type.
Add IOS.Gemini.SessionLength.{SessionType} and
IOS.Gemini.SessionLength.FRE.{SessionType} UMA histograms to record
session duration segmented by session type, for both general and First
Run Experience sessions.
(cherry picked from commit 099849eb20d0458e72cf7e5ee90cab0a0a5893ff)
Bug: 435649967, 436844461, 436839273
Change-Id: If594b026cf22f2699f47b90bbf9669f0fcb0e0a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6849516
Reviewed-by: Sebastien S-G <sebsg@chromium.org>
Commit-Queue: Ibrahim Kanouche <kanouche@google.com>
Auto-Submit: Ibrahim Kanouche <kanouche@google.com>
Reviewed-by: Nicolas MacBeth <nicolasmacbeth@google.com>
Cr-Original-Commit-Position: refs/heads/main@{#1501996}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6858552
Commit-Queue: Sebastien S-G <sebsg@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/7339@{#829}
Cr-Branched-From: 27be8b77710f4405fdfeb4ee946fcabb0f6c92b2-refs/heads/main@{#1496484}
diff --git a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm
index dcc715e..9ceae14 100644
--- a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm
+++ b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm
@@ -88,11 +88,17 @@
}
_didPresentBWGFRE = [self.delegate maybePresentBWGFRE];
- // Not presenting the FRE implies that the promo was shown and user consent
- // was given which means we can navigate to the BWG overlay immediately.
- if (!_didPresentBWGFRE) {
- [self prepareBWGOverlay];
+ if (_didPresentBWGFRE) {
+ BwgTabHelper* BWGTabHelper = [self activeWebStateBWGTabHelper];
+ if (!BWGTabHelper) {
+ return;
+ }
+ BWGTabHelper->SetIsFirstRun(true);
+
+ return;
}
+
+ [self prepareBWGOverlay];
}
#pragma mark - BWGConsentMutator
diff --git a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h
index 3d859696..fc2c362 100644
--- a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h
+++ b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h
@@ -64,6 +64,18 @@
// UMA histogram key for IOS.Gemini.Session.Time.
extern const char kBWGSessionTimeHistogram[];
+// UMA histogram key for IOS.Gemini.SessionLength.WithPrompt.
+extern const char kBWGSessionLengthWithPromptHistogram[];
+
+// UMA histogram key for IOS.Gemini.SessionLength.Abandoned.
+extern const char kBWGSessionLengthAbandonedHistogram[];
+
+// UMA histogram key for IOS.Gemini.SessionLength.FRE.WithPrompt.
+extern const char kBWGSessionLengthFREWithPromptHistogram[];
+
+// UMA histogram key for IOS.Gemini.SessionLength.FRE.Abandoned.
+extern const char kBWGSessionLengthFREWithAbandonedHistogram[];
+
// Enum for the IOS.Gemini.FirstPrompt.SubmissionMethod histogram.
// LINT.IfChange(IOSGeminiFirstPromptSubmissionMethod)
enum class IOSGeminiFirstPromptSubmissionMethod {
@@ -89,9 +101,22 @@
// UMA histogram key for IOS.Gemini.Response.Latency.WithoutContext.
extern const char kResponseLatencyWithoutContextHistogram[];
+// Represents the completed Gemini session types.
+enum class IOSGeminiSessionType {
+ kUnknown = 0,
+ kWithPrompt = 1,
+ kAbandoned = 2,
+ kMaxValue = kAbandoned,
+};
+
// Records the duration of a Gemini session.
void RecordBWGSessionTime(base::TimeDelta session_duration);
+// Records the duration of a Gemini session by type of session.
+void RecordBWGSessionLengthByType(base::TimeDelta session_duration,
+ bool is_first_run,
+ IOSGeminiSessionType session_type);
+
// Records when user sees the Gemini entry point impression.
// Can be called once every 10 minutes to avoid spam logging.
void RecordGeminiEntryPointImpression();
diff --git a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm
index 3d86bec..3af1779 100644
--- a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm
+++ b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm
@@ -35,6 +35,18 @@
const char kStartupTimeNoFREHistogram[] = "IOS.Gemini.StartupTime.NotFirstRun";
+const char kBWGSessionLengthWithPromptHistogram[] =
+ "IOS.Gemini.SessionLength.WithPrompt";
+
+const char kBWGSessionLengthAbandonedHistogram[] =
+ "IOS.Gemini.SessionLength.Abandoned";
+
+const char kBWGSessionLengthFREWithPromptHistogram[] =
+ "IOS.Gemini.SessionLength.FRE.WithPrompt";
+
+const char kBWGSessionLengthFREAbandonedHistogram[] =
+ "IOS.Gemini.SessionLength.FRE.Abandoned";
+
const char kBWGSessionTimeHistogram[] = "IOS.Gemini.Session.Time";
const char kFirstPromptSubmissionMethodHistogram[] =
@@ -88,6 +100,38 @@
base::UmaHistogramTimes(kBWGSessionTimeHistogram, session_duration);
}
+void RecordBWGSessionLengthByType(base::TimeDelta session_duration,
+ bool is_first_run,
+ IOSGeminiSessionType session_type) {
+ if (is_first_run) {
+ switch (session_type) {
+ case IOSGeminiSessionType::kWithPrompt:
+ base::UmaHistogramTimes(kBWGSessionLengthFREWithPromptHistogram,
+ session_duration);
+ break;
+ case IOSGeminiSessionType::kAbandoned:
+ base::UmaHistogramTimes(kBWGSessionLengthFREAbandonedHistogram,
+ session_duration);
+ break;
+ case IOSGeminiSessionType::kUnknown:
+ break;
+ }
+ } else {
+ switch (session_type) {
+ case IOSGeminiSessionType::kWithPrompt:
+ base::UmaHistogramTimes(kBWGSessionLengthWithPromptHistogram,
+ session_duration);
+ break;
+ case IOSGeminiSessionType::kAbandoned:
+ base::UmaHistogramTimes(kBWGSessionLengthAbandonedHistogram,
+ session_duration);
+ break;
+ case IOSGeminiSessionType::kUnknown:
+ break;
+ }
+ }
+}
+
void RecordGeminiEntryPointImpression() {
base::TimeTicks now = base::TimeTicks::Now();
base::TimeTicks& last_impression_time = GetLastGeminiImpressionTime();
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_session_handler.mm b/ios/chrome/browser/intelligence/bwg/model/bwg_session_handler.mm
index 4b8adb6..d25bebb 100644
--- a/ios/chrome/browser/intelligence/bwg/model/bwg_session_handler.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/bwg_session_handler.mm
@@ -92,10 +92,36 @@
serverID:(NSString*)serverID {
[_BWGHandler dismissBWGFlowWithCompletion:nil];
[self setSessionActive:NO clientID:clientID];
+
+ web::WebState* webState = [self webStateWithClientID:clientID];
+ if (!webState) {
+ return;
+ }
+ // Get the BWGTabHelper from the WebState.
+ BwgTabHelper* BWGTabHelper = BwgTabHelper::FromWebState(webState);
+ // WebState should always be valid as long as the tab is open.
+ if (!BWGTabHelper) {
+ // Early exit if no valid tab helper is found.
+ return;
+ }
+ bool isFirstSession = BWGTabHelper->GetIsFirstRun();
+ BWGTabHelper->SetIsFirstRun(false);
+
// Record session duration.
if (!_sessionStartTime.is_null()) {
base::TimeDelta session_duration =
base::TimeTicks::Now() - _sessionStartTime;
+
+ // Determine session type.
+ IOSGeminiSessionType session_type;
+ if (_hasSubmittedFirstPrompt) {
+ session_type = IOSGeminiSessionType::kWithPrompt;
+ } else {
+ session_type = IOSGeminiSessionType::kAbandoned;
+ }
+
+ RecordBWGSessionLengthByType(session_duration, isFirstSession,
+ session_type);
RecordBWGSessionTime(session_duration);
_sessionStartTime = base::TimeTicks();
}
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.h b/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.h
index 4d52348..881ca97 100644
--- a/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.h
+++ b/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.h
@@ -61,6 +61,12 @@
// Set the BWG commands handler, used to show/hide the BWG UI.
void SetBwgCommandsHandler(id<BWGCommands> handler);
+ // Sets the state of `is_first_run`.
+ void SetIsFirstRun(bool is_first_run);
+
+ // Gets the state of `is_first_run`.
+ bool GetIsFirstRun();
+
// WebStateObserver:
void WasShown(web::WebState* web_state) override;
void WasHidden(web::WebState* web_state) override;
@@ -116,6 +122,9 @@
// The observation of the Web State.
base::ScopedObservation<web::WebState, web::WebStateObserver>
web_state_observation_{this};
+
+ // Whether this is a first run experience.
+ bool is_first_run_ = false;
};
#endif // IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_BWG_TAB_HELPER_H_
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.mm b/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.mm
index dfc8771..c77d399 100644
--- a/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.mm
@@ -82,6 +82,14 @@
}
}
+void BwgTabHelper::SetIsFirstRun(bool is_first_run) {
+ is_first_run_ = is_first_run;
+}
+
+bool BwgTabHelper::GetIsFirstRun() {
+ return is_first_run_;
+}
+
bool BwgTabHelper::GetIsBwgSessionActiveInBackground() {
return is_bwg_session_active_in_background_;
}
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 4001b79..49e9142 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -2175,6 +2175,39 @@
</summary>
</histogram>
+<histogram name="IOS.Gemini.SessionLength.FRE.{SessionType}" units="ms"
+ expires_after="2026-02-13">
+ <owner>kanouche@google.com</owner>
+ <owner>bling-alchemy-eng@google.com</owner>
+ <summary>
+ The duration of a Gemini session that happens to be the first run, from when
+ the UI appears to when it is dismissed. This metric is segmented by the type
+ of sessions.
+ </summary>
+ <token key="SessionType">
+ <variant name="Abandoned"
+ summary="Sessions closed without any prompts submitted"/>
+ <variant name="WithPrompt"
+ summary="Sessions where the user submitted at least one prompt"/>
+ </token>
+</histogram>
+
+<histogram name="IOS.Gemini.SessionLength.{SessionType}" units="ms"
+ expires_after="2026-02-13">
+ <owner>kanouche@google.com</owner>
+ <owner>bling-alchemy-eng@google.com</owner>
+ <summary>
+ The duration of a Gemini session, from when the UI appears to when it is
+ dismissed. This metric is segmented by the type of sessions.
+ </summary>
+ <token key="SessionType">
+ <variant name="Abandoned"
+ summary="Sessions closed without any prompts submitted"/>
+ <variant name="WithPrompt"
+ summary="Sessions where the user submitted at least one prompt"/>
+ </token>
+</histogram>
+
<histogram name="IOS.Gemini.StartupTime.{FirstRunBool}" units="ms"
expires_after="2026-07-21">
<owner>nicolasmacbeth@google.com</owner>