| // 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 "content/browser/renderer_host/media/audio_service_listener.h" |
| |
| #include <utility> |
| |
| #include "base/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/time/default_tick_clock.h" |
| #include "content/browser/media/audio_log_factory.h" |
| #include "content/public/browser/child_process_data.h" |
| #include "content/public/browser/child_process_termination_info.h" |
| #include "content/public/common/content_features.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/audio/public/mojom/constants.mojom.h" |
| #include "services/audio/public/mojom/log_factory_manager.mojom.h" |
| |
| namespace content { |
| |
| AudioServiceListener::Metrics::Metrics(const base::TickClock* clock) |
| : clock_(clock), initial_downtime_start_(clock_->NowTicks()) {} |
| |
| AudioServiceListener::Metrics::~Metrics() = default; |
| |
| void AudioServiceListener::Metrics::ServiceAlreadyRunning() { |
| LogServiceStartStatus(ServiceStartStatus::kAlreadyStarted); |
| started_ = clock_->NowTicks(); |
| initial_downtime_start_ = base::TimeTicks(); |
| } |
| |
| void AudioServiceListener::Metrics::ServiceCreated() { |
| DCHECK(created_.is_null()); |
| created_ = clock_->NowTicks(); |
| } |
| |
| void AudioServiceListener::Metrics::ServiceStarted() { |
| started_ = clock_->NowTicks(); |
| |
| // |created_| is uninitialized if OnServiceCreated() was called before the |
| // listener is initialized with OnInit() call. |
| if (!created_.is_null()) { |
| LogServiceStartStatus(ServiceStartStatus::kSuccess); |
| UMA_HISTOGRAM_TIMES("Media.AudioService.ObservedStartupTime", |
| started_ - created_); |
| created_ = base::TimeTicks(); |
| } |
| |
| if (!initial_downtime_start_.is_null()) { |
| UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioService.ObservedInitialDowntime", |
| started_ - initial_downtime_start_, |
| base::TimeDelta(), base::TimeDelta::FromDays(7), |
| 50); |
| initial_downtime_start_ = base::TimeTicks(); |
| } |
| |
| if (!stopped_.is_null()) { |
| UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioService.ObservedDowntime2", |
| started_ - stopped_, base::TimeDelta(), |
| base::TimeDelta::FromDays(7), 50); |
| stopped_ = base::TimeTicks(); |
| } |
| } |
| |
| void AudioServiceListener::Metrics::ServiceFailedToStart() { |
| LogServiceStartStatus(ServiceStartStatus::kFailure); |
| created_ = base::TimeTicks(); |
| } |
| |
| void AudioServiceListener::Metrics::ServiceStopped() { |
| stopped_ = clock_->NowTicks(); |
| |
| DCHECK(!started_.is_null()); |
| UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioService.ObservedUptime", |
| stopped_ - started_, base::TimeDelta(), |
| base::TimeDelta::FromDays(7), 50); |
| started_ = base::TimeTicks(); |
| } |
| |
| void AudioServiceListener::Metrics::ServiceProcessTerminated( |
| Metrics::ServiceProcessTerminationStatus status) { |
| UMA_HISTOGRAM_ENUMERATION( |
| "Media.AudioService.ObservedProcessTerminationStatus", status); |
| } |
| |
| void AudioServiceListener::Metrics::LogServiceStartStatus( |
| Metrics::ServiceStartStatus status) { |
| UMA_HISTOGRAM_ENUMERATION("Media.AudioService.ObservedStartStatus", status); |
| } |
| |
| AudioServiceListener::AudioServiceListener( |
| std::unique_ptr<service_manager::Connector> connector) |
| : binding_(this), |
| connector_(std::move(connector)), |
| metrics_(base::DefaultTickClock::GetInstance()) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (!connector_) |
| return; // Happens in unittests. |
| |
| service_manager::mojom::ServiceManagerPtr service_manager; |
| connector_->BindInterface(service_manager::mojom::kServiceName, |
| &service_manager); |
| service_manager::mojom::ServiceManagerListenerPtr listener; |
| service_manager::mojom::ServiceManagerListenerRequest request( |
| mojo::MakeRequest(&listener)); |
| service_manager->AddListener(std::move(listener)); |
| binding_.Bind(std::move(request)); |
| BrowserChildProcessObserver::Add(this); |
| } |
| |
| AudioServiceListener::~AudioServiceListener() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| BrowserChildProcessObserver::Remove(this); |
| } |
| |
| base::ProcessId AudioServiceListener::GetProcessId() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| return process_id_; |
| } |
| |
| void AudioServiceListener::OnInit( |
| std::vector<service_manager::mojom::RunningServiceInfoPtr> |
| running_services) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| for (const service_manager::mojom::RunningServiceInfoPtr& instance : |
| running_services) { |
| if (instance->identity.name() == audio::mojom::kServiceName) { |
| process_id_ = instance->pid; |
| metrics_.ServiceAlreadyRunning(); |
| MaybeSetLogFactory(); |
| break; |
| } |
| } |
| } |
| |
| void AudioServiceListener::OnServiceCreated( |
| service_manager::mojom::RunningServiceInfoPtr service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (service->identity.name() != audio::mojom::kServiceName) |
| return; |
| metrics_.ServiceCreated(); |
| MaybeSetLogFactory(); |
| } |
| |
| void AudioServiceListener::OnServiceStarted( |
| const ::service_manager::Identity& identity, |
| uint32_t pid) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (identity.name() != audio::mojom::kServiceName) |
| return; |
| process_id_ = pid; |
| metrics_.ServiceStarted(); |
| } |
| |
| void AudioServiceListener::OnServicePIDReceived( |
| const ::service_manager::Identity& identity, |
| uint32_t pid) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (identity.name() != audio::mojom::kServiceName) |
| return; |
| process_id_ = pid; |
| } |
| |
| void AudioServiceListener::OnServiceFailedToStart( |
| const ::service_manager::Identity& identity) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (identity.name() != audio::mojom::kServiceName) |
| return; |
| metrics_.ServiceFailedToStart(); |
| } |
| |
| void AudioServiceListener::OnServiceStopped( |
| const ::service_manager::Identity& identity) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (identity.name() != audio::mojom::kServiceName) |
| return; |
| metrics_.ServiceStopped(); |
| log_factory_is_set_ = false; |
| } |
| |
| void AudioServiceListener::BrowserChildProcessHostDisconnected( |
| const ChildProcessData& data) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (base::GetProcId(data.handle) != process_id_) |
| return; |
| process_id_ = base::kNullProcessId; |
| metrics_.ServiceProcessTerminated( |
| Metrics::ServiceProcessTerminationStatus::kDisconnect); |
| } |
| |
| void AudioServiceListener::BrowserChildProcessCrashed( |
| const ChildProcessData& data, |
| const ChildProcessTerminationInfo& info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (base::GetProcId(data.handle) != process_id_) |
| return; |
| process_id_ = base::kNullProcessId; |
| metrics_.ServiceProcessTerminated( |
| Metrics::ServiceProcessTerminationStatus::kCrash); |
| } |
| |
| void AudioServiceListener::BrowserChildProcessKilled( |
| const ChildProcessData& data, |
| const ChildProcessTerminationInfo& info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (base::GetProcId(data.handle) != process_id_) |
| return; |
| process_id_ = base::kNullProcessId; |
| metrics_.ServiceProcessTerminated( |
| Metrics::ServiceProcessTerminationStatus::kKill); |
| } |
| |
| void AudioServiceListener::MaybeSetLogFactory() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_); |
| if (!base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess) || |
| !connector_ || log_factory_is_set_) |
| return; |
| |
| media::mojom::AudioLogFactoryPtr audio_log_factory_ptr; |
| mojo::MakeStrongBinding(std::make_unique<AudioLogFactory>(), |
| mojo::MakeRequest(&audio_log_factory_ptr)); |
| audio::mojom::LogFactoryManagerPtr log_factory_manager_ptr; |
| connector_->BindInterface(audio::mojom::kServiceName, |
| mojo::MakeRequest(&log_factory_manager_ptr)); |
| log_factory_manager_ptr->SetLogFactory(std::move(audio_log_factory_ptr)); |
| log_factory_is_set_ = true; |
| } |
| |
| } // namespace content |