1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/metrics/tab_stats_tracker.h"
6 
7 #include <algorithm>
8 #include <string>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/power_monitor/power_monitor.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
21 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
22 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
23 #include "chrome/browser/resource_coordinator/tab_manager.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_list.h"
26 #include "chrome/browser/ui/browser_window.h"
27 #include "chrome/browser/ui/tabs/tab_strip_model.h"
28 #include "chrome/common/buildflags.h"
29 #include "chrome/common/pref_names.h"
30 #include "components/metrics/daily_event.h"
31 #include "components/prefs/pref_registry_simple.h"
32 #include "components/prefs/pref_service.h"
33 #include "components/ukm/content/source_url_recorder.h"
34 #include "services/metrics/public/cpp/ukm_builders.h"
35 #include "services/metrics/public/cpp/ukm_source_id.h"
36 #include "ui/gfx/geometry/rect.h"
37 #include "ui/gfx/geometry/size.h"
38 
39 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
40 #include "chrome/browser/background/background_mode_manager.h"
41 #endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
42 
43 namespace metrics {
44 
45 namespace {
46 
47 // The interval at which the DailyEvent::CheckInterval function should be
48 // called.
49 constexpr base::TimeDelta kDailyEventIntervalTimeDelta =
50     base::TimeDelta::FromMinutes(30);
51 
52 // The intervals at which we report the number of unused tabs. This is used for
53 // all the tab usage histograms listed below.
54 //
55 // The 'Tabs.TabUsageIntervalLength' histogram suffixes entry in histograms.xml
56 // should be kept in sync with these values.
57 constexpr base::TimeDelta kTabUsageReportingIntervals[] = {
58     base::TimeDelta::FromSeconds(30), base::TimeDelta::FromMinutes(1),
59     base::TimeDelta::FromMinutes(10), base::TimeDelta::FromHours(1),
60     base::TimeDelta::FromHours(5),    base::TimeDelta::FromHours(12)};
61 
62 #if defined(OS_WIN)
63 const base::TimeDelta kNativeWindowOcclusionCalculationInterval =
64     base::TimeDelta::FromMinutes(10);
65 #endif
66 
67 // The interval at which the heartbeat tab metrics should be reported.
68 const base::TimeDelta kTabsHeartbeatReportingInterval =
69     base::TimeDelta::FromMinutes(5);
70 
71 // The global TabStatsTracker instance.
72 TabStatsTracker* g_tab_stats_tracker_instance = nullptr;
73 
74 // Ensure that an interval is a valid one (i.e. listed in
75 // |kTabUsageReportingIntervals|).
IsValidInterval(base::TimeDelta interval)76 bool IsValidInterval(base::TimeDelta interval) {
77   return base::Contains(kTabUsageReportingIntervals, interval);
78 }
79 
80 }  // namespace
81 
82 // static
83 const char TabStatsTracker::kTabStatsDailyEventHistogramName[] =
84     "Tabs.TabsStatsDailyEventInterval";
85 const char TabStatsTracker::UmaStatsReportingDelegate::
86     kNumberOfTabsOnResumeHistogramName[] = "Tabs.NumberOfTabsOnResume";
87 const char
88     TabStatsTracker::UmaStatsReportingDelegate::kMaxTabsInADayHistogramName[] =
89         "Tabs.MaxTabsInADay";
90 const char TabStatsTracker::UmaStatsReportingDelegate::
91     kMaxTabsPerWindowInADayHistogramName[] = "Tabs.MaxTabsPerWindowInADay";
92 const char TabStatsTracker::UmaStatsReportingDelegate::
93     kMaxWindowsInADayHistogramName[] = "Tabs.MaxWindowsInADay";
94 
95 // Tab usage histograms.
96 const char TabStatsTracker::UmaStatsReportingDelegate::
97     kUnusedAndClosedInIntervalHistogramNameBase[] =
98         "Tabs.UnusedAndClosedInInterval.Count";
99 const char TabStatsTracker::UmaStatsReportingDelegate::
100     kUnusedTabsInIntervalHistogramNameBase[] = "Tabs.UnusedInInterval.Count";
101 const char TabStatsTracker::UmaStatsReportingDelegate::
102     kUsedAndClosedInIntervalHistogramNameBase[] =
103         "Tabs.UsedAndClosedInInterval.Count";
104 const char TabStatsTracker::UmaStatsReportingDelegate::
105     kUsedTabsInIntervalHistogramNameBase[] = "Tabs.UsedInInterval.Count";
106 
107 const char
108     TabStatsTracker::UmaStatsReportingDelegate::kTabCountHistogramName[] =
109         "Tabs.TabCount";
110 const char
111     TabStatsTracker::UmaStatsReportingDelegate::kWindowCountHistogramName[] =
112         "Tabs.WindowCount";
113 
114 const char
115     TabStatsTracker::UmaStatsReportingDelegate::kWindowWidthHistogramName[] =
116         "Tabs.WindowWidth";
117 
tab_stats() const118 const TabStatsDataStore::TabsStats& TabStatsTracker::tab_stats() const {
119   return tab_stats_data_store_->tab_stats();
120 }
121 
TabStatsTracker(PrefService * pref_service)122 TabStatsTracker::TabStatsTracker(PrefService* pref_service)
123     : reporting_delegate_(std::make_unique<UmaStatsReportingDelegate>()),
124       delegate_(std::make_unique<TabStatsTrackerDelegate>()),
125       tab_stats_data_store_(std::make_unique<TabStatsDataStore>(pref_service)),
126       daily_event_(
127           std::make_unique<DailyEvent>(pref_service,
128                                        ::prefs::kTabStatsDailySample,
129                                        kTabStatsDailyEventHistogramName)) {
130   DCHECK(pref_service);
131   // Get the list of existing windows/tabs. There shouldn't be any if this is
132   // initialized at startup but this will ensure that the counts stay accurate
133   // if the initialization gets moved to after the creation of the first tab.
134   BrowserList* browser_list = BrowserList::GetInstance();
135   for (Browser* browser : *browser_list) {
136     OnBrowserAdded(browser);
137     for (int i = 0; i < browser->tab_strip_model()->count(); ++i)
138       OnInitialOrInsertedTab(browser->tab_strip_model()->GetWebContentsAt(i));
139     tab_stats_data_store_->UpdateMaxTabsPerWindowIfNeeded(
140         static_cast<size_t>(browser->tab_strip_model()->count()));
141   }
142 
143   browser_list->AddObserver(this);
144   base::PowerMonitor::AddObserver(this);
145 
146   daily_event_->AddObserver(std::make_unique<TabStatsDailyObserver>(
147       reporting_delegate_.get(), tab_stats_data_store_.get()));
148   // Call the CheckInterval method to see if the data need to be immediately
149   // reported.
150   daily_event_->CheckInterval();
151   daily_event_timer_.Start(FROM_HERE, kDailyEventIntervalTimeDelta,
152                            daily_event_.get(), &DailyEvent::CheckInterval);
153 
154   // Initialize the interval maps and timers associated with them.
155   for (base::TimeDelta interval : kTabUsageReportingIntervals) {
156     TabStatsDataStore::TabsStateDuringIntervalMap* interval_map =
157         tab_stats_data_store_->AddInterval();
158     // Setup the timer associated with this interval.
159     std::unique_ptr<base::RepeatingTimer> timer =
160         std::make_unique<base::RepeatingTimer>();
161     timer->Start(
162         FROM_HERE, interval,
163         base::BindRepeating(&TabStatsTracker::OnInterval,
164                             base::Unretained(this), interval, interval_map));
165     usage_interval_timers_.push_back(std::move(timer));
166   }
167 
168 // The native window occlusion calculation is specific to Windows.
169 #if defined(OS_WIN)
170   native_window_occlusion_timer_.Start(
171       FROM_HERE, kNativeWindowOcclusionCalculationInterval,
172       base::BindRepeating(
173           &TabStatsTracker::CalculateAndRecordNativeWindowVisibilities,
174           base::Unretained(this)));
175 #endif
176 
177   heartbeat_timer_.Start(FROM_HERE, kTabsHeartbeatReportingInterval,
178                          base::BindRepeating(&TabStatsTracker::OnHeartbeatEvent,
179                                              base::Unretained(this)));
180 }
181 
~TabStatsTracker()182 TabStatsTracker::~TabStatsTracker() {
183   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
184   BrowserList::GetInstance()->RemoveObserver(this);
185 }
186 
187 // static
SetInstance(std::unique_ptr<TabStatsTracker> instance)188 void TabStatsTracker::SetInstance(std::unique_ptr<TabStatsTracker> instance) {
189   DCHECK_EQ(nullptr, g_tab_stats_tracker_instance);
190   g_tab_stats_tracker_instance = instance.release();
191 }
192 
GetInstance()193 TabStatsTracker* TabStatsTracker::GetInstance() {
194   return g_tab_stats_tracker_instance;
195 }
196 
RegisterPrefs(PrefRegistrySimple * registry)197 void TabStatsTracker::RegisterPrefs(PrefRegistrySimple* registry) {
198   registry->RegisterIntegerPref(::prefs::kTabStatsTotalTabCountMax, 0);
199   registry->RegisterIntegerPref(::prefs::kTabStatsMaxTabsPerWindow, 0);
200   registry->RegisterIntegerPref(::prefs::kTabStatsWindowCountMax, 0);
201   DailyEvent::RegisterPref(registry, ::prefs::kTabStatsDailySample);
202 }
203 
SetDelegateForTesting(std::unique_ptr<TabStatsTrackerDelegate> new_delegate)204 void TabStatsTracker::SetDelegateForTesting(
205     std::unique_ptr<TabStatsTrackerDelegate> new_delegate) {
206   delegate_ = std::move(new_delegate);
207 }
208 
OnDailyEvent(DailyEvent::IntervalType type)209 void TabStatsTracker::TabStatsDailyObserver::OnDailyEvent(
210     DailyEvent::IntervalType type) {
211   reporting_delegate_->ReportDailyMetrics(data_store_->tab_stats());
212   data_store_->ResetMaximumsToCurrentState();
213 }
214 
215 class TabStatsTracker::WebContentsUsageObserver
216     : public content::WebContentsObserver {
217  public:
WebContentsUsageObserver(content::WebContents * web_contents,TabStatsTracker * tab_stats_tracker)218   WebContentsUsageObserver(content::WebContents* web_contents,
219                            TabStatsTracker* tab_stats_tracker)
220       : content::WebContentsObserver(web_contents),
221         tab_stats_tracker_(tab_stats_tracker),
222         ukm_source_id_(ukm::GetSourceIdForWebContentsDocument(web_contents)) {}
223 
224   // content::WebContentsObserver:
DidStartNavigation(content::NavigationHandle * navigation_handle)225   void DidStartNavigation(
226       content::NavigationHandle* navigation_handle) override {
227     // Treat browser-initiated navigations as user interactions.
228     if (!navigation_handle->IsRendererInitiated()) {
229       tab_stats_tracker_->tab_stats_data_store()->OnTabInteraction(
230           web_contents());
231     }
232   }
233 
DidFinishNavigation(content::NavigationHandle * navigation_handle)234   void DidFinishNavigation(
235       content::NavigationHandle* navigation_handle) override {
236     if (!navigation_handle->HasCommitted() ||
237         !navigation_handle->IsInMainFrame() ||
238         navigation_handle->IsSameDocument()) {
239       return;
240     }
241     // Update navigation time for UKM reporting.
242     navigation_time_ = navigation_handle->NavigationStart();
243     ukm_source_id_ = ukm::ConvertToSourceId(
244         navigation_handle->GetNavigationId(), ukm::SourceIdType::NAVIGATION_ID);
245   }
246 
DidGetUserInteraction(const blink::WebInputEvent & event)247   void DidGetUserInteraction(const blink::WebInputEvent& event) override {
248     tab_stats_tracker_->tab_stats_data_store()->OnTabInteraction(
249         web_contents());
250   }
251 
OnVisibilityChanged(content::Visibility visibility)252   void OnVisibilityChanged(content::Visibility visibility) override {
253     if (visibility == content::Visibility::VISIBLE)
254       tab_stats_tracker_->tab_stats_data_store()->OnTabVisible(web_contents());
255   }
256 
WebContentsDestroyed()257   void WebContentsDestroyed() override {
258     if (ukm_source_id_) {
259       ukm::builders::TabManager_TabLifetime(ukm_source_id_)
260           .SetTimeSinceNavigation(
261               (base::TimeTicks::Now() - navigation_time_).InMilliseconds())
262           .Record(ukm::UkmRecorder::Get());
263     }
264 
265     tab_stats_tracker_->OnWebContentsDestroyed(web_contents());
266     // The call above will free |this| and so nothing should be done on this
267     // object starting from here.
268   }
269 
270  private:
271   TabStatsTracker* tab_stats_tracker_;
272   // The last navigation time associated with this tab.
273   base::TimeTicks navigation_time_ = base::TimeTicks::Now();
274   // Updated when a navigation is finished.
275   ukm::SourceId ukm_source_id_ = 0;
276 
277   DISALLOW_COPY_AND_ASSIGN(WebContentsUsageObserver);
278 };
279 
OnBrowserAdded(Browser * browser)280 void TabStatsTracker::OnBrowserAdded(Browser* browser) {
281   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
282   tab_stats_data_store_->OnWindowAdded();
283   browser->tab_strip_model()->AddObserver(this);
284 }
285 
OnBrowserRemoved(Browser * browser)286 void TabStatsTracker::OnBrowserRemoved(Browser* browser) {
287   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
288   tab_stats_data_store_->OnWindowRemoved();
289   browser->tab_strip_model()->RemoveObserver(this);
290 }
291 
OnTabStripModelChanged(TabStripModel * tab_strip_model,const TabStripModelChange & change,const TabStripSelectionChange & selection)292 void TabStatsTracker::OnTabStripModelChanged(
293     TabStripModel* tab_strip_model,
294     const TabStripModelChange& change,
295     const TabStripSelectionChange& selection) {
296   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
297   if (change.type() == TabStripModelChange::kInserted) {
298     for (const auto& contents : change.GetInsert()->contents)
299       OnInitialOrInsertedTab(contents.contents);
300 
301     tab_stats_data_store_->UpdateMaxTabsPerWindowIfNeeded(
302         static_cast<size_t>(tab_strip_model->count()));
303 
304     return;
305   }
306 
307   if (change.type() == TabStripModelChange::kReplaced) {
308     auto* replace = change.GetReplace();
309     tab_stats_data_store_->OnTabReplaced(replace->old_contents,
310                                          replace->new_contents);
311     web_contents_usage_observers_.insert(std::make_pair(
312         replace->new_contents, std::make_unique<WebContentsUsageObserver>(
313                                    replace->new_contents, this)));
314     web_contents_usage_observers_.erase(replace->old_contents);
315   }
316 }
317 
TabChangedAt(content::WebContents * web_contents,int index,TabChangeType change_type)318 void TabStatsTracker::TabChangedAt(content::WebContents* web_contents,
319                                    int index,
320                                    TabChangeType change_type) {
321   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
322   // Ignore 'loading' and 'title' changes, we're only interested in audio here.
323   if (change_type != TabChangeType::kAll)
324     return;
325   if (web_contents->IsCurrentlyAudible())
326     tab_stats_data_store_->OnTabAudible(web_contents);
327 }
328 
OnResume()329 void TabStatsTracker::OnResume() {
330   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
331   reporting_delegate_->ReportTabCountOnResume(
332       tab_stats_data_store_->tab_stats().total_tab_count);
333 }
334 
OnInterval(base::TimeDelta interval,TabStatsDataStore::TabsStateDuringIntervalMap * interval_map)335 void TabStatsTracker::OnInterval(
336     base::TimeDelta interval,
337     TabStatsDataStore::TabsStateDuringIntervalMap* interval_map) {
338   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
339   DCHECK(interval_map);
340   reporting_delegate_->ReportUsageDuringInterval(*interval_map, interval);
341   // Reset the interval data.
342   tab_stats_data_store_->ResetIntervalData(interval_map);
343 }
344 
OnInitialOrInsertedTab(content::WebContents * web_contents)345 void TabStatsTracker::OnInitialOrInsertedTab(
346     content::WebContents* web_contents) {
347   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
348   // If we already have a WebContentsObserver for this tab then it means that
349   // it's already tracked and it's being dragged into a new window, there's
350   // nothing to do here.
351   if (!base::Contains(web_contents_usage_observers_, web_contents)) {
352     tab_stats_data_store_->OnTabAdded(web_contents);
353     web_contents_usage_observers_.insert(std::make_pair(
354         web_contents,
355         std::make_unique<WebContentsUsageObserver>(web_contents, this)));
356   }
357 }
358 
OnWebContentsDestroyed(content::WebContents * web_contents)359 void TabStatsTracker::OnWebContentsDestroyed(
360     content::WebContents* web_contents) {
361   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
362   DCHECK(base::Contains(web_contents_usage_observers_, web_contents));
363   web_contents_usage_observers_.erase(
364       web_contents_usage_observers_.find(web_contents));
365   tab_stats_data_store_->OnTabRemoved(web_contents);
366 }
367 
OnHeartbeatEvent()368 void TabStatsTracker::OnHeartbeatEvent() {
369   reporting_delegate_->ReportHeartbeatMetrics(
370       tab_stats_data_store_->tab_stats());
371 }
372 
ReportTabCountOnResume(size_t tab_count)373 void TabStatsTracker::UmaStatsReportingDelegate::ReportTabCountOnResume(
374     size_t tab_count) {
375   // Don't report the number of tabs on resume if Chrome is running in
376   // background with no visible window.
377   if (IsChromeBackgroundedWithoutWindows())
378     return;
379   UMA_HISTOGRAM_COUNTS_10000(kNumberOfTabsOnResumeHistogramName, tab_count);
380 }
381 
ReportDailyMetrics(const TabStatsDataStore::TabsStats & tab_stats)382 void TabStatsTracker::UmaStatsReportingDelegate::ReportDailyMetrics(
383     const TabStatsDataStore::TabsStats& tab_stats) {
384   // Don't report the counts if they're equal to 0, this means that Chrome has
385   // only been running in the background since the last time the metrics have
386   // been reported.
387   if (tab_stats.total_tab_count_max == 0)
388     return;
389   UMA_HISTOGRAM_COUNTS_10000(kMaxTabsInADayHistogramName,
390                              tab_stats.total_tab_count_max);
391   UMA_HISTOGRAM_COUNTS_10000(kMaxTabsPerWindowInADayHistogramName,
392                              tab_stats.max_tab_per_window);
393   UMA_HISTOGRAM_COUNTS_10000(kMaxWindowsInADayHistogramName,
394                              tab_stats.window_count_max);
395 }
396 
ReportHeartbeatMetrics(const TabStatsDataStore::TabsStats & tab_stats)397 void TabStatsTracker::UmaStatsReportingDelegate::ReportHeartbeatMetrics(
398     const TabStatsDataStore::TabsStats& tab_stats) {
399   // Don't report anything if Chrome is running in background with no visible
400   // window.
401   if (IsChromeBackgroundedWithoutWindows())
402     return;
403 
404   UMA_HISTOGRAM_COUNTS_10000(kTabCountHistogramName, tab_stats.total_tab_count);
405   UMA_HISTOGRAM_COUNTS_10000(kWindowCountHistogramName, tab_stats.window_count);
406 
407   // Record the width of all open browser windows with tabs.
408   for (Browser* browser : *BrowserList::GetInstance()) {
409     if (browser->type() != Browser::TYPE_NORMAL)
410       continue;
411 
412     const BrowserWindow* window = browser->window();
413 
414     // Only consider visible windows.
415     if (!window->IsVisible() || window->IsMinimized())
416       continue;
417 
418     // Get the window's size (in DIPs).
419     const gfx::Size window_size = browser->window()->GetBounds().size();
420 
421     // If the size is for some reason 0 in either dimension, skip it.
422     if (window_size.IsEmpty())
423       continue;
424 
425     // A 4K screen is 4096 pixels wide. Doubling this and rounding up to
426     // 10000 should give a reasonable upper bound on DIPs. For the
427     // minimum width, pick an arbitrary value of 100. Most screens are
428     // unlikely to be this small, and likewise a browser window's min
429     // width is around this size.
430     UMA_HISTOGRAM_CUSTOM_COUNTS(kWindowWidthHistogramName, window_size.width(),
431                                 100, 10000, 50);
432   }
433 }
434 
ReportUsageDuringInterval(const TabStatsDataStore::TabsStateDuringIntervalMap & interval_map,base::TimeDelta interval)435 void TabStatsTracker::UmaStatsReportingDelegate::ReportUsageDuringInterval(
436     const TabStatsDataStore::TabsStateDuringIntervalMap& interval_map,
437     base::TimeDelta interval) {
438   // Counts the number of used/unused tabs during this interval, a tabs counts
439   // as unused if it hasn't been interacted with or visible during the duration
440   // of the interval.
441   size_t used_tabs = 0;
442   size_t used_and_closed_tabs = 0;
443   size_t unused_tabs = 0;
444   size_t unused_and_closed_tabs = 0;
445   for (const auto& iter : interval_map) {
446     // There's currently no distinction between a visible/audible tab and one
447     // that has been interacted with in these metrics.
448     // TODO(sebmarchand): Add a metric that track the number of tab that have
449     // been visible/audible but not interacted with during an interval,
450     // https://crbug.com/800828.
451     if (iter.second.interacted_during_interval ||
452         iter.second.visible_or_audible_during_interval) {
453       if (iter.second.exists_currently)
454         ++used_tabs;
455       else
456         ++used_and_closed_tabs;
457     } else {
458       if (iter.second.exists_currently)
459         ++unused_tabs;
460       else
461         ++unused_and_closed_tabs;
462     }
463   }
464 
465   std::string used_and_closed_histogram_name = GetIntervalHistogramName(
466       UmaStatsReportingDelegate::kUsedAndClosedInIntervalHistogramNameBase,
467       interval);
468   std::string used_histogram_name = GetIntervalHistogramName(
469       UmaStatsReportingDelegate::kUsedTabsInIntervalHistogramNameBase,
470       interval);
471   std::string unused_and_closed_histogram_name = GetIntervalHistogramName(
472       UmaStatsReportingDelegate::kUnusedAndClosedInIntervalHistogramNameBase,
473       interval);
474   std::string unused_histogram_name = GetIntervalHistogramName(
475       UmaStatsReportingDelegate::kUnusedTabsInIntervalHistogramNameBase,
476       interval);
477 
478   base::UmaHistogramCounts10000(used_and_closed_histogram_name,
479                                 used_and_closed_tabs);
480   base::UmaHistogramCounts10000(used_histogram_name, used_tabs);
481   base::UmaHistogramCounts10000(unused_and_closed_histogram_name,
482                                 unused_and_closed_tabs);
483   base::UmaHistogramCounts10000(unused_histogram_name, unused_tabs);
484 }
485 
486 // static
487 std::string
GetIntervalHistogramName(const char * base_name,base::TimeDelta interval)488 TabStatsTracker::UmaStatsReportingDelegate::GetIntervalHistogramName(
489     const char* base_name,
490     base::TimeDelta interval) {
491   DCHECK(IsValidInterval(interval));
492   return base::StringPrintf("%s_%zu", base_name,
493                             static_cast<size_t>(interval.InSeconds()));
494 }
495 
496 bool TabStatsTracker::UmaStatsReportingDelegate::
IsChromeBackgroundedWithoutWindows()497     IsChromeBackgroundedWithoutWindows() {
498 #if BUILDFLAG(ENABLE_BACKGROUND_MODE)
499   if (g_browser_process && g_browser_process->background_mode_manager()
500                                ->IsBackgroundWithoutWindows()) {
501     return true;
502   }
503 #endif  // BUILDFLAG(ENABLE_BACKGROUND_MODE)
504   return false;
505 }
506 
507 }  // namespace metrics
508