Commit 9a01b0bc authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Audio Focus] Add UMA metrics

Add UMA metrics for tracking audio focus events. There are three
histograms added in total; the first records RequestAudioFocus
calls, the second records AbandonAudioFocus calls and the third
records requested AudioFocusTypes.

In the future we will probably switch these with UKM but UKM is
not available for ARC++ yet.

BUG=883958

Change-Id: I1092b9d1c638a13526687ac77b87d66490e4b89c
Reviewed-on: https://chromium-review.googlesource.com/c/1238871
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarBrian White <bcwhite@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597337}
parent 9e26abc0
...@@ -14,6 +14,8 @@ source_set("lib") { ...@@ -14,6 +14,8 @@ source_set("lib") {
sources = [ sources = [
"audio_focus_manager.cc", "audio_focus_manager.cc",
"audio_focus_manager.h", "audio_focus_manager.h",
"audio_focus_manager_metrics_helper.cc",
"audio_focus_manager_metrics_helper.h",
"media_session_service.cc", "media_session_service.cc",
"media_session_service.h", "media_session_service.h",
] ]
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/containers/adapters.h" #include "base/containers/adapters.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/interface_request.h"
#include "services/media_session/audio_focus_manager_metrics_helper.h"
#include "services/media_session/public/cpp/switches.h" #include "services/media_session/public/cpp/switches.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h" #include "services/media_session/public/mojom/audio_focus.mojom.h"
...@@ -38,6 +39,7 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -38,6 +39,7 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
const std::string& source_name) const std::string& source_name)
: id_(id), : id_(id),
source_name_(source_name), source_name_(source_name),
metrics_helper_(source_name),
session_(std::move(session)), session_(std::move(session)),
session_info_(std::move(session_info)), session_info_(std::move(session_info)),
audio_focus_type_(audio_focus_type), audio_focus_type_(audio_focus_type),
...@@ -50,6 +52,10 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -50,6 +52,10 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
session_.set_connection_error_handler( session_.set_connection_error_handler(
base::BindOnce(&AudioFocusManager::StackRow::OnConnectionError, base::BindOnce(&AudioFocusManager::StackRow::OnConnectionError,
base::Unretained(this))); base::Unretained(this)));
metrics_helper_.OnRequestAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kInitial,
audio_focus_type);
} }
~StackRow() override = default; ~StackRow() override = default;
...@@ -73,9 +79,18 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -73,9 +79,18 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
owner_->RequestAudioFocusInternal(std::move(row), type, owner_->RequestAudioFocusInternal(std::move(row), type,
std::move(callback)); std::move(callback));
metrics_helper_.OnRequestAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kUpdate,
audio_focus_type_);
} }
void AbandonAudioFocus() override { owner_->AbandonAudioFocusInternal(id_); } void AbandonAudioFocus() override {
metrics_helper_.OnAbandonAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::kAPI);
owner_->AbandonAudioFocusInternal(id_);
}
void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override { void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override {
session_info_ = std::move(info); session_info_ = std::move(info);
...@@ -110,6 +125,17 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -110,6 +125,17 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
private: private:
void OnConnectionError() { void OnConnectionError() {
// Since we have multiple pathways that can call |OnConnectionError| we
// should use the |encountered_error_| bit to make sure we abandon focus
// just the first time.
if (encountered_error_)
return;
encountered_error_ = true;
metrics_helper_.OnAbandonAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::
kConnectionError);
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&AudioFocusManager::AbandonAudioFocusInternal, FROM_HERE, base::BindOnce(&AudioFocusManager::AbandonAudioFocusInternal,
base::Unretained(owner_), id_)); base::Unretained(owner_), id_));
...@@ -118,6 +144,9 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient { ...@@ -118,6 +144,9 @@ class AudioFocusManager::StackRow : public mojom::AudioFocusRequestClient {
const RequestId id_; const RequestId id_;
const std::string source_name_; const std::string source_name_;
AudioFocusManagerMetricsHelper metrics_helper_;
bool encountered_error_ = false;
mojom::MediaSessionPtr session_; mojom::MediaSessionPtr session_;
mojom::MediaSessionInfoPtr session_info_; mojom::MediaSessionInfoPtr session_info_;
mojom::AudioFocusType audio_focus_type_; mojom::AudioFocusType audio_focus_type_;
......
// Copyright 2018 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 "services/media_session/audio_focus_manager_metrics_helper.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/strings/string_util.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
namespace media_session {
namespace {
static const char kHistogramPrefix[] = "Media.Session.AudioFocus.";
static const char kHistogramSeparator[] = ".";
static const char kRequestAudioFocusName[] = "Request";
static const char kAudioFocusTypeName[] = "Type";
static const char kAbandonAudioFocusName[] = "Abandon";
static constexpr base::HistogramBase::Sample kHistogramMinimum = 1;
} // namespace
AudioFocusManagerMetricsHelper::AudioFocusManagerMetricsHelper(
const std::string& source_name)
: source_name_(source_name),
request_source_histogram_(GetHistogram(
kRequestAudioFocusName,
static_cast<Sample>(AudioFocusRequestSource::kMaxValue))),
focus_type_histogram_(
GetHistogram(kAudioFocusTypeName,
static_cast<Sample>(AudioFocusType::kMaxValue))),
abandon_source_histogram_(GetHistogram(
kAbandonAudioFocusName,
static_cast<Sample>(AudioFocusAbandonSource::kMaxValue))) {}
AudioFocusManagerMetricsHelper::~AudioFocusManagerMetricsHelper() = default;
void AudioFocusManagerMetricsHelper::OnRequestAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource source,
mojom::AudioFocusType type) {
if (!ShouldRecordMetrics())
return;
request_source_histogram_->Add(static_cast<Sample>(source));
focus_type_histogram_->Add(static_cast<Sample>(FromMojoFocusType(type)));
}
void AudioFocusManagerMetricsHelper::OnAbandonAudioFocus(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource source) {
if (!ShouldRecordMetrics())
return;
abandon_source_histogram_->Add(static_cast<Sample>(source));
}
base::HistogramBase* AudioFocusManagerMetricsHelper::GetHistogram(
const char* name,
Sample max) const {
std::string histogram_name;
histogram_name.append(kHistogramPrefix);
histogram_name.append(name);
histogram_name.append(kHistogramSeparator);
// This will ensure that |source_name| starts with an upper case letter.
for (auto it = source_name_.begin(); it < source_name_.end(); ++it) {
if (it == source_name_.begin())
histogram_name.push_back(base::ToUpperASCII(*it));
else
histogram_name.push_back(*it);
}
return base::LinearHistogram::FactoryGet(histogram_name, kHistogramMinimum,
max, max + 1,
base::HistogramBase::kNoFlags);
}
// static
AudioFocusManagerMetricsHelper::AudioFocusType
AudioFocusManagerMetricsHelper::FromMojoFocusType(mojom::AudioFocusType type) {
switch (type) {
case mojom::AudioFocusType::kGain:
return AudioFocusType::kGain;
case mojom::AudioFocusType::kGainTransientMayDuck:
return AudioFocusType::kGainTransientMayDuck;
case mojom::AudioFocusType::kGainTransient:
return AudioFocusType::kGainTransient;
}
NOTREACHED();
return AudioFocusType::kUnknown;
}
bool AudioFocusManagerMetricsHelper::ShouldRecordMetrics() const {
return !source_name_.empty();
}
} // namespace media_session
// Copyright 2018 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 SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_METRICS_HELPER_H_
#define SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_METRICS_HELPER_H_
#include <string>
#include "base/macros.h"
#include "base/metrics/histogram_base.h"
namespace media_session {
namespace mojom {
enum class AudioFocusType;
} // namespace mojom
class AudioFocusManagerMetricsHelper {
public:
using Sample = base::HistogramBase::Sample;
AudioFocusManagerMetricsHelper(const std::string& source_name);
~AudioFocusManagerMetricsHelper();
// This is used for UMA histogram
// (Media.Session.AudioFocus.*.RequestAudioFocus). New values should be
// appended only and update |kMaxValue|.
enum class AudioFocusRequestSource {
kUnknown = 0,
kInitial = 1,
kUpdate = 2,
kMaxValue = kUpdate // Leave at the end.
};
// This is used for UMA histogram
// (Media.Session.AudioFocus.*.AbandonAudioFocus). New values should be
// appended only and update |kMaxValue|.
enum class AudioFocusAbandonSource {
kUnknown = 0,
kAPI = 1,
kConnectionError = 2,
kMaxValue = kConnectionError // Leave at the end.
};
// This is used for UMA histogram
// (Media.Session.AudioFocus.*.AudioFocusType). New values should be
// appended only and update |kMaxValue|. It should mirror the
// media_session::mojom::AudioFocusType enum.
enum class AudioFocusType {
kUnknown = 0,
kGain = 1,
kGainTransientMayDuck = 2,
kGainTransient = 3,
kMaxValue = kGainTransient // Leave at the end.
};
void OnRequestAudioFocus(AudioFocusRequestSource, mojom::AudioFocusType);
void OnAbandonAudioFocus(AudioFocusAbandonSource);
private:
static AudioFocusType FromMojoFocusType(mojom::AudioFocusType);
base::HistogramBase* GetHistogram(const char* name, Sample max) const;
bool ShouldRecordMetrics() const;
const std::string& source_name_;
base::HistogramBase* const request_source_histogram_;
base::HistogramBase* const focus_type_histogram_;
base::HistogramBase* const abandon_source_histogram_;
DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerMetricsHelper);
};
} // namespace media_session
#endif // SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_METRICS_HELPER_H_
...@@ -11,10 +11,12 @@ ...@@ -11,10 +11,12 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h" #include "base/test/scoped_command_line.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/interface_request.h"
#include "services/media_session/audio_focus_manager_metrics_helper.h"
#include "services/media_session/media_session_service.h" #include "services/media_session/media_session_service.h"
#include "services/media_session/public/cpp/switches.h" #include "services/media_session/public/cpp/switches.h"
#include "services/media_session/public/cpp/test/audio_focus_test_util.h" #include "services/media_session/public/cpp/test/audio_focus_test_util.h"
...@@ -316,6 +318,17 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> { ...@@ -316,6 +318,17 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> {
return requests.back()->source_name.value(); return requests.back()->source_name.value();
} }
std::unique_ptr<base::HistogramSamples> GetHistogramSamplesSinceTestStart(
const std::string& name) {
return histogram_tester_.GetHistogramSamplesSinceCreation(name);
}
int GetAudioFocusHistogramCount() {
return histogram_tester_
.GetTotalCountsForPrefix("Media.Session.AudioFocus.")
.size();
}
private: private:
int GetCountForType(mojom::AudioFocusType type) { int GetCountForType(mojom::AudioFocusType type) {
const auto audio_focus_requests = GetRequests(); const auto audio_focus_requests = GetRequests();
...@@ -382,6 +395,7 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> { ...@@ -382,6 +395,7 @@ class AudioFocusManagerTest : public testing::TestWithParam<bool> {
std::unique_ptr<service_manager::Connector> connector_; std::unique_ptr<service_manager::Connector> connector_;
mojom::AudioFocusManagerPtr audio_focus_ptr_; mojom::AudioFocusManagerPtr audio_focus_ptr_;
mojom::AudioFocusManagerDebugPtr audio_focus_debug_ptr_; mojom::AudioFocusManagerDebugPtr audio_focus_debug_ptr_;
base::HistogramTester histogram_tester_;
DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerTest); DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerTest);
}; };
...@@ -979,4 +993,102 @@ TEST_P(AudioFocusManagerTest, SourceName_Updated) { ...@@ -979,4 +993,102 @@ TEST_P(AudioFocusManagerTest, SourceName_Updated) {
EXPECT_EQ(kExampleSourceName, GetSourceNameForLastRequest()); EXPECT_EQ(kExampleSourceName, GetSourceNameForLastRequest());
} }
TEST_P(AudioFocusManagerTest, RecordUmaMetrics) {
EXPECT_EQ(0, GetAudioFocusHistogramCount());
SetSourceName(kExampleSourceName);
MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Request.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::
kInitial)));
}
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Type.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(
1,
samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusType::kGainTransient)));
}
EXPECT_EQ(2, GetAudioFocusHistogramCount());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Request.Test"));
EXPECT_EQ(2, samples->TotalCount());
EXPECT_EQ(
1,
samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kUpdate)));
}
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Type.Test"));
EXPECT_EQ(2, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusType::kGain)));
}
EXPECT_EQ(2, GetAudioFocusHistogramCount());
AbandonAudioFocus(&media_session);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Abandon.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(
1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::kAPI)));
}
EXPECT_EQ(3, GetAudioFocusHistogramCount());
}
TEST_P(AudioFocusManagerTest, RecordUmaMetrics_ConnectionError) {
SetSourceName(kExampleSourceName);
{
MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
}
MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Abandon.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::
kConnectionError)));
}
}
TEST_P(AudioFocusManagerTest, RecordUmaMetrics_NoSourceName) {
MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetAudioFocusHistogramCount());
}
} // namespace media_session } // namespace media_session
...@@ -2147,6 +2147,25 @@ uploading your change for review. These are checked by presubmit scripts. ...@@ -2147,6 +2147,25 @@ uploading your change for review. These are checked by presubmit scripts.
<int value="54" label="Sub mute"/> <int value="54" label="Sub mute"/>
</enum> </enum>
<enum name="AudioFocusAbandonSource">
<int value="0" label="Unknown"/>
<int value="1" label="API"/>
<int value="2" label="Connection Error"/>
</enum>
<enum name="AudioFocusRequestSource">
<int value="0" label="Unknown"/>
<int value="1" label="Initial request"/>
<int value="2" label="Update existing request"/>
</enum>
<enum name="AudioFocusType">
<int value="0" label="Unknown"/>
<int value="1" label="Gain"/>
<int value="2" label="Gain transient may duck"/>
<int value="3" label="Gain transient"/>
</enum>
<enum name="AudioFramesPerBuffer"> <enum name="AudioFramesPerBuffer">
<obsolete> <obsolete>
Removed from code Sep 2014. Removed from code Sep 2014.
...@@ -44432,6 +44432,39 @@ uploading your change for review. ...@@ -44432,6 +44432,39 @@ uploading your change for review.
</summary> </summary>
</histogram> </histogram>
<histogram name="Media.Session.AudioFocus.Abandon"
enum="AudioFocusAbandonSource" expires_after="2019-10-03">
<owner>beccahughes@chromium.org</owner>
<owner>media-dev@chromium.org</owner>
<summary>
The number of times a media session abandon audio focus and where the
abandon audio focus call originated from. This is recorded every time a
media session abandons audio focus because it has stopped playing.
</summary>
</histogram>
<histogram name="Media.Session.AudioFocus.Request"
enum="AudioFocusRequestSource" expires_after="2019-10-03">
<owner>beccahughes@chromium.org</owner>
<owner>media-dev@chromium.org</owner>
<summary>
The number of times a media session requested audio focus and where that
focus request originated from. This is recorded every time a media session
requests audio focus because it wants to start playing.
</summary>
</histogram>
<histogram name="Media.Session.AudioFocus.Type" enum="AudioFocusType">
<owner>beccahughes@chromium.org</owner>
<owner>media-dev@chromium.org</owner>
<summary>
The number of times a media session requested audio focus for a specific
audio focus type. This is recorded every time a media session requests a new
audio focus type. This may be because the session has started playing or the
type of media being played has changed.
</summary>
</histogram>
<histogram name="Media.Session.Pause" enum="MediaSessionActionSource"> <histogram name="Media.Session.Pause" enum="MediaSessionActionSource">
<owner>avayvod@chromium.org</owner> <owner>avayvod@chromium.org</owner>
<owner>mlamouri@chromium.org</owner> <owner>mlamouri@chromium.org</owner>
...@@ -125697,6 +125730,14 @@ uploading your change for review. ...@@ -125697,6 +125730,14 @@ uploading your change for review.
<affected-histogram name="MediaRouter.Cast.App.Availability"/> <affected-histogram name="MediaRouter.Cast.App.Availability"/>
</histogram_suffixes> </histogram_suffixes>
<histogram_suffixes name="MediaSessionSource" separator=".">
<suffix name="Arc" label="ARC++ app"/>
<suffix name="Web" label="Website"/>
<affected-histogram name="Media.Session.AudioFocus.Abandon"/>
<affected-histogram name="Media.Session.AudioFocus.Request"/>
<affected-histogram name="Media.Session.AudioFocus.Type"/>
</histogram_suffixes>
<histogram_suffixes name="MediaStreamAndDecoderType" separator="."> <histogram_suffixes name="MediaStreamAndDecoderType" separator=".">
<suffix name="Audio.HW" label="Platform audio decoder"/> <suffix name="Audio.HW" label="Platform audio decoder"/>
<suffix name="Audio.SW" label="Software audio decoder"/> <suffix name="Audio.SW" label="Software audio decoder"/>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment