// Copyright 2014 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 "chrome/browser/metrics/chromeos_metrics_provider.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/common/pref_names.h" #include "chromeos/system/statistics_provider.h" #include "components/metrics/metrics_service.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/browser_thread.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_device.h" #include "ui/events/event_utils.h" #include "ui/gfx/screen.h" #if defined(USE_X11) #include "ui/events/devices/x11/touch_factory_x11.h" #endif // defined(USE_X11) using metrics::ChromeUserMetricsExtension; using metrics::SampledProfile; using metrics::SystemProfileProto; typedef SystemProfileProto::Hardware::Bluetooth::PairedDevice PairedDevice; namespace { PairedDevice::Type AsBluetoothDeviceType( device::BluetoothDevice::DeviceType device_type) { switch (device_type) { case device::BluetoothDevice::DEVICE_UNKNOWN: return PairedDevice::DEVICE_UNKNOWN; case device::BluetoothDevice::DEVICE_COMPUTER: return PairedDevice::DEVICE_COMPUTER; case device::BluetoothDevice::DEVICE_PHONE: return PairedDevice::DEVICE_PHONE; case device::BluetoothDevice::DEVICE_MODEM: return PairedDevice::DEVICE_MODEM; case device::BluetoothDevice::DEVICE_AUDIO: return PairedDevice::DEVICE_AUDIO; case device::BluetoothDevice::DEVICE_CAR_AUDIO: return PairedDevice::DEVICE_CAR_AUDIO; case device::BluetoothDevice::DEVICE_VIDEO: return PairedDevice::DEVICE_VIDEO; case device::BluetoothDevice::DEVICE_PERIPHERAL: return PairedDevice::DEVICE_PERIPHERAL; case device::BluetoothDevice::DEVICE_JOYSTICK: return PairedDevice::DEVICE_JOYSTICK; case device::BluetoothDevice::DEVICE_GAMEPAD: return PairedDevice::DEVICE_GAMEPAD; case device::BluetoothDevice::DEVICE_KEYBOARD: return PairedDevice::DEVICE_KEYBOARD; case device::BluetoothDevice::DEVICE_MOUSE: return PairedDevice::DEVICE_MOUSE; case device::BluetoothDevice::DEVICE_TABLET: return PairedDevice::DEVICE_TABLET; case device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO: return PairedDevice::DEVICE_KEYBOARD_MOUSE_COMBO; } NOTREACHED(); return PairedDevice::DEVICE_UNKNOWN; } void WriteExternalTouchscreensProto(SystemProfileProto::Hardware* hardware) { #if defined(USE_X11) std::set > touchscreen_ids = ui::TouchFactory::GetInstance()->GetTouchscreenIds(); for (std::set >::iterator it = touchscreen_ids.begin(); it != touchscreen_ids.end(); ++it) { SystemProfileProto::Hardware::TouchScreen* touchscreen = hardware->add_external_touchscreen(); touchscreen->set_vendor_id(it->first); touchscreen->set_product_id(it->second); } #endif // defined(USE_X11) } void IncrementPrefValue(const char* path) { PrefService* pref = g_browser_process->local_state(); DCHECK(pref); int value = pref->GetInteger(path); pref->SetInteger(path, value + 1); } } // namespace ChromeOSMetricsProvider::ChromeOSMetricsProvider() : registered_user_count_at_log_initialization_(false), user_count_at_log_initialization_(0), weak_ptr_factory_(this) { } ChromeOSMetricsProvider::~ChromeOSMetricsProvider() { } // static void ChromeOSMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0); registry->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0); registry->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount, 0); } // static void ChromeOSMetricsProvider::LogCrash(const std::string& crash_type) { if (crash_type == "user") IncrementPrefValue(prefs::kStabilityOtherUserCrashCount); else if (crash_type == "kernel") IncrementPrefValue(prefs::kStabilityKernelCrashCount); else if (crash_type == "uncleanshutdown") IncrementPrefValue(prefs::kStabilitySystemUncleanShutdownCount); else NOTREACHED() << "Unexpected Chrome OS crash type " << crash_type; // Wake up metrics logs sending if necessary now that new // log data is available. g_browser_process->metrics_service()->OnApplicationNotIdle(); } ChromeOSMetricsProvider::EnrollmentStatus ChromeOSMetricsProvider::GetEnrollmentStatus() { policy::BrowserPolicyConnectorChromeOS* connector = g_browser_process->platform_part()->browser_policy_connector_chromeos(); if (!connector) return ERROR_GETTING_ENROLLMENT_STATUS; return connector->IsEnterpriseManaged() ? MANAGED : NON_MANAGED; } void ChromeOSMetricsProvider::OnDidCreateMetricsLog() { registered_user_count_at_log_initialization_ = false; if (user_manager::UserManager::IsInitialized()) { registered_user_count_at_log_initialization_ = true; user_count_at_log_initialization_ = user_manager::UserManager::Get()->GetLoggedInUsers().size(); } } void ChromeOSMetricsProvider::InitTaskGetHardwareClass( const base::Closure& callback) { // Run the (potentially expensive) task on the FILE thread to avoid blocking // the UI thread. content::BrowserThread::PostTaskAndReply( content::BrowserThread::FILE, FROM_HERE, base::Bind(&ChromeOSMetricsProvider::InitTaskGetHardwareClassOnFileThread, weak_ptr_factory_.GetWeakPtr()), callback); } void ChromeOSMetricsProvider::InitTaskGetHardwareClassOnFileThread() { DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic( "hardware_class", &hardware_class_); } void ChromeOSMetricsProvider::ProvideSystemProfileMetrics( metrics::SystemProfileProto* system_profile_proto) { WriteBluetoothProto(system_profile_proto); UpdateMultiProfileUserCount(system_profile_proto); metrics::SystemProfileProto::Hardware* hardware = system_profile_proto->mutable_hardware(); hardware->set_hardware_class(hardware_class_); gfx::Display::TouchSupport has_touch = ui::GetInternalDisplayTouchSupport(); if (has_touch == gfx::Display::TOUCH_SUPPORT_AVAILABLE) hardware->set_internal_display_supports_touch(true); else if (has_touch == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE) hardware->set_internal_display_supports_touch(false); WriteExternalTouchscreensProto(hardware); } void ChromeOSMetricsProvider::ProvideStabilityMetrics( metrics::SystemProfileProto* system_profile_proto) { metrics::SystemProfileProto::Stability* stability_proto = system_profile_proto->mutable_stability(); PrefService* pref = g_browser_process->local_state(); int count = pref->GetInteger(prefs::kStabilityOtherUserCrashCount); if (count) { stability_proto->set_other_user_crash_count(count); pref->SetInteger(prefs::kStabilityOtherUserCrashCount, 0); } count = pref->GetInteger(prefs::kStabilityKernelCrashCount); if (count) { stability_proto->set_kernel_crash_count(count); pref->SetInteger(prefs::kStabilityKernelCrashCount, 0); } count = pref->GetInteger(prefs::kStabilitySystemUncleanShutdownCount); if (count) { stability_proto->set_unclean_system_shutdown_count(count); pref->SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 0); } } void ChromeOSMetricsProvider::ProvideGeneralMetrics( metrics::ChromeUserMetricsExtension* uma_proto) { std::vector sampled_profiles; if (perf_provider_.GetSampledProfiles(&sampled_profiles)) { for (std::vector::iterator iter = sampled_profiles.begin(); iter != sampled_profiles.end(); ++iter) { uma_proto->add_sampled_profile()->Swap(&(*iter)); } } RecordEnrollmentStatus(); } void ChromeOSMetricsProvider::WriteBluetoothProto( metrics::SystemProfileProto* system_profile_proto) { metrics::SystemProfileProto::Hardware* hardware = system_profile_proto->mutable_hardware(); // BluetoothAdapterFactory::GetAdapter is synchronous on Chrome OS; if that // changes this will fail at the DCHECK(). device::BluetoothAdapterFactory::GetAdapter(base::Bind( &ChromeOSMetricsProvider::SetBluetoothAdapter, base::Unretained(this))); DCHECK(adapter_.get()); SystemProfileProto::Hardware::Bluetooth* bluetooth = hardware->mutable_bluetooth(); bluetooth->set_is_present(adapter_->IsPresent()); bluetooth->set_is_enabled(adapter_->IsPowered()); device::BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); for (device::BluetoothAdapter::DeviceList::iterator iter = devices.begin(); iter != devices.end(); ++iter) { device::BluetoothDevice* device = *iter; // Don't collect information about LE devices yet. if (!device->IsPaired()) continue; PairedDevice* paired_device = bluetooth->add_paired_device(); paired_device->set_bluetooth_class(device->GetBluetoothClass()); paired_device->set_type(AsBluetoothDeviceType(device->GetDeviceType())); // |address| is xx:xx:xx:xx:xx:xx, extract the first three components and // pack into a uint32. std::string address = device->GetAddress(); if (address.size() > 9 && address[2] == ':' && address[5] == ':' && address[8] == ':') { std::string vendor_prefix_str; uint64 vendor_prefix; base::RemoveChars(address.substr(0, 9), ":", &vendor_prefix_str); DCHECK_EQ(6U, vendor_prefix_str.size()); base::HexStringToUInt64(vendor_prefix_str, &vendor_prefix); paired_device->set_vendor_prefix(vendor_prefix); } switch (device->GetVendorIDSource()) { case device::BluetoothDevice::VENDOR_ID_BLUETOOTH: paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_BLUETOOTH); break; case device::BluetoothDevice::VENDOR_ID_USB: paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_USB); break; default: paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_UNKNOWN); } paired_device->set_vendor_id(device->GetVendorID()); paired_device->set_product_id(device->GetProductID()); paired_device->set_device_id(device->GetDeviceID()); } } void ChromeOSMetricsProvider::UpdateMultiProfileUserCount( metrics::SystemProfileProto* system_profile_proto) { if (user_manager::UserManager::IsInitialized()) { size_t user_count = user_manager::UserManager::Get()->GetLoggedInUsers().size(); // We invalidate the user count if it changed while the log was open. if (registered_user_count_at_log_initialization_ && user_count != user_count_at_log_initialization_) { user_count = 0; } system_profile_proto->set_multi_profile_user_count(user_count); } } void ChromeOSMetricsProvider::SetBluetoothAdapter( scoped_refptr adapter) { adapter_ = adapter; } void ChromeOSMetricsProvider::RecordEnrollmentStatus() { UMA_HISTOGRAM_ENUMERATION( "UMA.EnrollmentStatus", GetEnrollmentStatus(), ENROLLMENT_STATUS_MAX); }