blob: a6a8551fd13cc66d6d4bac3fc9e4a6427e753d33 [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.
#include "components/arc/metrics/arc_metrics_service.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "components/arc/arc_bridge_service.h"
namespace arc {
namespace {
constexpr base::TimeDelta kUmaMinTime = base::TimeDelta::FromMilliseconds(1);
constexpr base::TimeDelta kUmaMaxTime = base::TimeDelta::FromSeconds(60);
constexpr int kUmaNumBuckets = 50;
constexpr base::TimeDelta kRequestProcessListPeriod =
base::TimeDelta::FromMinutes(5);
constexpr char kArcProcessNamePrefix[] = "org.chromium.arc.";
constexpr char kGmsProcessNamePrefix[] = "com.google.android.gms";
constexpr char kBootProgressEnableScreen[] = "boot_progress_enable_screen";
std::string BootTypeToString(mojom::BootType boot_type) {
switch (boot_type) {
case mojom::BootType::UNKNOWN:
break;
case mojom::BootType::FIRST_BOOT:
return ".FirstBoot";
case mojom::BootType::FIRST_BOOT_AFTER_UPDATE:
return ".FirstBootAfterUpdate";
case mojom::BootType::REGULAR_BOOT:
return ".RegularBoot";
}
NOTREACHED();
return "";
}
} // namespace
ArcMetricsService::ArcMetricsService(ArcBridgeService* bridge_service)
: ArcService(bridge_service),
binding_(this),
process_observer_(this),
weak_ptr_factory_(this) {
arc_bridge_service()->metrics()->AddObserver(this);
arc_bridge_service()->process()->AddObserver(&process_observer_);
}
ArcMetricsService::~ArcMetricsService() {
DCHECK(CalledOnValidThread());
arc_bridge_service()->process()->RemoveObserver(&process_observer_);
arc_bridge_service()->metrics()->RemoveObserver(this);
}
bool ArcMetricsService::CalledOnValidThread() {
// Make sure access to the Chrome clipboard is happening in the UI thread.
return thread_checker_.CalledOnValidThread();
}
void ArcMetricsService::OnInstanceReady() {
VLOG(2) << "Start metrics service.";
// Retrieve ARC start time from session manager.
chromeos::SessionManagerClient* session_manager_client =
chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
session_manager_client->GetArcStartTime(
base::Bind(&ArcMetricsService::OnArcStartTimeRetrieved,
weak_ptr_factory_.GetWeakPtr()));
}
void ArcMetricsService::OnInstanceClosed() {
VLOG(2) << "Close metrics service.";
DCHECK(CalledOnValidThread());
if (binding_.is_bound())
binding_.Unbind();
}
void ArcMetricsService::OnProcessInstanceReady() {
VLOG(2) << "Start updating process list.";
timer_.Start(FROM_HERE, kRequestProcessListPeriod, this,
&ArcMetricsService::RequestProcessList);
}
void ArcMetricsService::OnProcessInstanceClosed() {
VLOG(2) << "Stop updating process list.";
timer_.Stop();
}
void ArcMetricsService::RequestProcessList() {
mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service()->process(), RequestProcessList);
if (!process_instance)
return;
VLOG(2) << "RequestProcessList";
process_instance->RequestProcessList(base::Bind(
&ArcMetricsService::ParseProcessList, weak_ptr_factory_.GetWeakPtr()));
}
void ArcMetricsService::ParseProcessList(
std::vector<mojom::RunningAppProcessInfoPtr> processes) {
int running_app_count = 0;
for (const auto& process : processes) {
const std::string& process_name = process->process_name;
const mojom::ProcessState& process_state = process->process_state;
// Processes like the ARC launcher and intent helper are always running
// and not counted as apps running by users. With the same reasoning,
// GMS (Google Play Services) and its related processes are skipped as
// well. The process_state check below filters out system processes,
// services, apps that are cached because they've run before.
if (base::StartsWith(process_name, kArcProcessNamePrefix,
base::CompareCase::SENSITIVE) ||
base::StartsWith(process_name, kGmsProcessNamePrefix,
base::CompareCase::SENSITIVE) ||
process_state != mojom::ProcessState::TOP) {
VLOG(2) << "Skipped " << process_name << " " << process_state;
} else {
++running_app_count;
}
}
UMA_HISTOGRAM_COUNTS_100("Arc.AppCount", running_app_count);
}
void ArcMetricsService::OnArcStartTimeRetrieved(
bool success,
base::TimeTicks arc_start_time) {
DCHECK(CalledOnValidThread());
if (!success) {
LOG(ERROR) << "Failed to retrieve ARC start timeticks.";
return;
}
auto* instance =
ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->metrics(), Init);
if (!instance)
return;
// The binding of host interface is deferred until the ARC start time is
// retrieved here because it prevents race condition of the ARC start
// time availability in ReportBootProgress().
if (!binding_.is_bound()) {
mojom::MetricsHostPtr host_ptr;
binding_.Bind(mojo::MakeRequest(&host_ptr));
instance->Init(std::move(host_ptr));
}
arc_start_time_ = arc_start_time;
VLOG(2) << "ARC start @" << arc_start_time_;
}
void ArcMetricsService::ReportBootProgress(
std::vector<mojom::BootProgressEventPtr> events,
mojom::BootType boot_type) {
DCHECK(CalledOnValidThread());
if (boot_type == mojom::BootType::UNKNOWN) {
LOG(WARNING) << "boot_type is unknown. Skip recording UMA.";
return;
}
int64_t arc_start_time_in_ms =
(arc_start_time_ - base::TimeTicks()).InMilliseconds();
const std::string suffix = BootTypeToString(boot_type);
for (const auto& event : events) {
VLOG(2) << "Report boot progress event:" << event->event << "@"
<< event->uptimeMillis;
const std::string name = "Arc." + event->event + suffix;
const base::TimeDelta elapsed_time = base::TimeDelta::FromMilliseconds(
event->uptimeMillis - arc_start_time_in_ms);
base::UmaHistogramCustomTimes(name, elapsed_time, kUmaMinTime, kUmaMaxTime,
kUmaNumBuckets);
if (event->event.compare(kBootProgressEnableScreen) == 0) {
base::UmaHistogramCustomTimes("Arc.AndroidBootTime" + suffix,
elapsed_time, kUmaMinTime, kUmaMaxTime,
kUmaNumBuckets);
}
}
}
ArcMetricsService::ProcessObserver::ProcessObserver(
ArcMetricsService* arc_metrics_service)
: arc_metrics_service_(arc_metrics_service) {}
ArcMetricsService::ProcessObserver::~ProcessObserver() = default;
void ArcMetricsService::ProcessObserver::OnInstanceReady() {
arc_metrics_service_->OnProcessInstanceReady();
}
void ArcMetricsService::ProcessObserver::OnInstanceClosed() {
arc_metrics_service_->OnProcessInstanceClosed();
}
} // namespace arc