1 // Copyright 2014 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/chromeos_metrics_provider.h" 6 7 #include "base/prefs/pref_registry_simple.h" 8 #include "base/prefs/pref_service.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_util.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/common/pref_names.h" 14 #include "chromeos/system/statistics_provider.h" 15 #include "components/metrics/metrics_service.h" 16 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" 17 #include "components/user_manager/user_manager.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "device/bluetooth/bluetooth_adapter.h" 20 #include "device/bluetooth/bluetooth_adapter_factory.h" 21 #include "device/bluetooth/bluetooth_device.h" 22 #include "ui/events/event_utils.h" 23 #include "ui/gfx/screen.h" 24 25 #if defined(USE_X11) 26 #include "ui/events/x/touch_factory_x11.h" 27 #endif // defined(USE_X11) 28 29 using metrics::ChromeUserMetricsExtension; 30 using metrics::SampledProfile; 31 using metrics::SystemProfileProto; 32 typedef SystemProfileProto::Hardware::Bluetooth::PairedDevice PairedDevice; 33 34 namespace { 35 36 PairedDevice::Type AsBluetoothDeviceType( 37 device::BluetoothDevice::DeviceType device_type) { 38 switch (device_type) { 39 case device::BluetoothDevice::DEVICE_UNKNOWN: 40 return PairedDevice::DEVICE_UNKNOWN; 41 case device::BluetoothDevice::DEVICE_COMPUTER: 42 return PairedDevice::DEVICE_COMPUTER; 43 case device::BluetoothDevice::DEVICE_PHONE: 44 return PairedDevice::DEVICE_PHONE; 45 case device::BluetoothDevice::DEVICE_MODEM: 46 return PairedDevice::DEVICE_MODEM; 47 case device::BluetoothDevice::DEVICE_AUDIO: 48 return PairedDevice::DEVICE_AUDIO; 49 case device::BluetoothDevice::DEVICE_CAR_AUDIO: 50 return PairedDevice::DEVICE_CAR_AUDIO; 51 case device::BluetoothDevice::DEVICE_VIDEO: 52 return PairedDevice::DEVICE_VIDEO; 53 case device::BluetoothDevice::DEVICE_PERIPHERAL: 54 return PairedDevice::DEVICE_PERIPHERAL; 55 case device::BluetoothDevice::DEVICE_JOYSTICK: 56 return PairedDevice::DEVICE_JOYSTICK; 57 case device::BluetoothDevice::DEVICE_GAMEPAD: 58 return PairedDevice::DEVICE_GAMEPAD; 59 case device::BluetoothDevice::DEVICE_KEYBOARD: 60 return PairedDevice::DEVICE_KEYBOARD; 61 case device::BluetoothDevice::DEVICE_MOUSE: 62 return PairedDevice::DEVICE_MOUSE; 63 case device::BluetoothDevice::DEVICE_TABLET: 64 return PairedDevice::DEVICE_TABLET; 65 case device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO: 66 return PairedDevice::DEVICE_KEYBOARD_MOUSE_COMBO; 67 } 68 69 NOTREACHED(); 70 return PairedDevice::DEVICE_UNKNOWN; 71 } 72 73 void WriteExternalTouchscreensProto(SystemProfileProto::Hardware* hardware) { 74 #if defined(USE_X11) 75 std::set<std::pair<int, int> > touchscreen_ids = 76 ui::TouchFactory::GetInstance()->GetTouchscreenIds(); 77 for (std::set<std::pair<int, int> >::iterator it = touchscreen_ids.begin(); 78 it != touchscreen_ids.end(); 79 ++it) { 80 SystemProfileProto::Hardware::TouchScreen* touchscreen = 81 hardware->add_external_touchscreen(); 82 touchscreen->set_vendor_id(it->first); 83 touchscreen->set_product_id(it->second); 84 } 85 #endif // defined(USE_X11) 86 } 87 88 void IncrementPrefValue(const char* path) { 89 PrefService* pref = g_browser_process->local_state(); 90 DCHECK(pref); 91 int value = pref->GetInteger(path); 92 pref->SetInteger(path, value + 1); 93 } 94 95 } // namespace 96 97 ChromeOSMetricsProvider::ChromeOSMetricsProvider() 98 : registered_user_count_at_log_initialization_(false), 99 user_count_at_log_initialization_(0), 100 weak_ptr_factory_(this) { 101 } 102 103 ChromeOSMetricsProvider::~ChromeOSMetricsProvider() { 104 } 105 106 // static 107 void ChromeOSMetricsProvider::RegisterPrefs(PrefRegistrySimple* registry) { 108 registry->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0); 109 registry->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0); 110 registry->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount, 0); 111 } 112 113 // static 114 void ChromeOSMetricsProvider::LogCrash(const std::string& crash_type) { 115 if (crash_type == "user") 116 IncrementPrefValue(prefs::kStabilityOtherUserCrashCount); 117 else if (crash_type == "kernel") 118 IncrementPrefValue(prefs::kStabilityKernelCrashCount); 119 else if (crash_type == "uncleanshutdown") 120 IncrementPrefValue(prefs::kStabilitySystemUncleanShutdownCount); 121 else 122 NOTREACHED() << "Unexpected Chrome OS crash type " << crash_type; 123 124 // Wake up metrics logs sending if necessary now that new 125 // log data is available. 126 g_browser_process->metrics_service()->OnApplicationNotIdle(); 127 } 128 129 void ChromeOSMetricsProvider::OnDidCreateMetricsLog() { 130 registered_user_count_at_log_initialization_ = false; 131 if (user_manager::UserManager::IsInitialized()) { 132 registered_user_count_at_log_initialization_ = true; 133 user_count_at_log_initialization_ = 134 user_manager::UserManager::Get()->GetLoggedInUsers().size(); 135 } 136 } 137 138 void ChromeOSMetricsProvider::InitTaskGetHardwareClass( 139 const base::Closure& callback) { 140 // Run the (potentially expensive) task on the FILE thread to avoid blocking 141 // the UI thread. 142 content::BrowserThread::PostTaskAndReply( 143 content::BrowserThread::FILE, 144 FROM_HERE, 145 base::Bind(&ChromeOSMetricsProvider::InitTaskGetHardwareClassOnFileThread, 146 weak_ptr_factory_.GetWeakPtr()), 147 callback); 148 } 149 150 void ChromeOSMetricsProvider::InitTaskGetHardwareClassOnFileThread() { 151 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 152 chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic( 153 "hardware_class", &hardware_class_); 154 } 155 156 void ChromeOSMetricsProvider::ProvideSystemProfileMetrics( 157 metrics::SystemProfileProto* system_profile_proto) { 158 WriteBluetoothProto(system_profile_proto); 159 UpdateMultiProfileUserCount(system_profile_proto); 160 161 metrics::SystemProfileProto::Hardware* hardware = 162 system_profile_proto->mutable_hardware(); 163 hardware->set_hardware_class(hardware_class_); 164 gfx::Display::TouchSupport has_touch = ui::GetInternalDisplayTouchSupport(); 165 if (has_touch == gfx::Display::TOUCH_SUPPORT_AVAILABLE) 166 hardware->set_internal_display_supports_touch(true); 167 else if (has_touch == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE) 168 hardware->set_internal_display_supports_touch(false); 169 WriteExternalTouchscreensProto(hardware); 170 } 171 172 void ChromeOSMetricsProvider::ProvideStabilityMetrics( 173 metrics::SystemProfileProto* system_profile_proto) { 174 metrics::SystemProfileProto::Stability* stability_proto = 175 system_profile_proto->mutable_stability(); 176 PrefService* pref = g_browser_process->local_state(); 177 int count = pref->GetInteger(prefs::kStabilityOtherUserCrashCount); 178 if (count) { 179 stability_proto->set_other_user_crash_count(count); 180 pref->SetInteger(prefs::kStabilityOtherUserCrashCount, 0); 181 } 182 183 count = pref->GetInteger(prefs::kStabilityKernelCrashCount); 184 if (count) { 185 stability_proto->set_kernel_crash_count(count); 186 pref->SetInteger(prefs::kStabilityKernelCrashCount, 0); 187 } 188 189 count = pref->GetInteger(prefs::kStabilitySystemUncleanShutdownCount); 190 if (count) { 191 stability_proto->set_unclean_system_shutdown_count(count); 192 pref->SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 0); 193 } 194 } 195 196 void ChromeOSMetricsProvider::ProvideGeneralMetrics( 197 metrics::ChromeUserMetricsExtension* uma_proto) { 198 std::vector<SampledProfile> sampled_profiles; 199 if (perf_provider_.GetSampledProfiles(&sampled_profiles)) { 200 for (std::vector<SampledProfile>::iterator iter = sampled_profiles.begin(); 201 iter != sampled_profiles.end(); 202 ++iter) { 203 uma_proto->add_sampled_profile()->Swap(&(*iter)); 204 } 205 } 206 } 207 208 void ChromeOSMetricsProvider::WriteBluetoothProto( 209 metrics::SystemProfileProto* system_profile_proto) { 210 metrics::SystemProfileProto::Hardware* hardware = 211 system_profile_proto->mutable_hardware(); 212 213 // BluetoothAdapterFactory::GetAdapter is synchronous on Chrome OS; if that 214 // changes this will fail at the DCHECK(). 215 device::BluetoothAdapterFactory::GetAdapter(base::Bind( 216 &ChromeOSMetricsProvider::SetBluetoothAdapter, base::Unretained(this))); 217 DCHECK(adapter_.get()); 218 219 SystemProfileProto::Hardware::Bluetooth* bluetooth = 220 hardware->mutable_bluetooth(); 221 222 bluetooth->set_is_present(adapter_->IsPresent()); 223 bluetooth->set_is_enabled(adapter_->IsPowered()); 224 225 device::BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); 226 for (device::BluetoothAdapter::DeviceList::iterator iter = devices.begin(); 227 iter != devices.end(); 228 ++iter) { 229 device::BluetoothDevice* device = *iter; 230 // Don't collect information about LE devices yet. 231 if (!device->IsPaired()) 232 continue; 233 234 PairedDevice* paired_device = bluetooth->add_paired_device(); 235 paired_device->set_bluetooth_class(device->GetBluetoothClass()); 236 paired_device->set_type(AsBluetoothDeviceType(device->GetDeviceType())); 237 238 // |address| is xx:xx:xx:xx:xx:xx, extract the first three components and 239 // pack into a uint32. 240 std::string address = device->GetAddress(); 241 if (address.size() > 9 && address[2] == ':' && address[5] == ':' && 242 address[8] == ':') { 243 std::string vendor_prefix_str; 244 uint64 vendor_prefix; 245 246 base::RemoveChars(address.substr(0, 9), ":", &vendor_prefix_str); 247 DCHECK_EQ(6U, vendor_prefix_str.size()); 248 base::HexStringToUInt64(vendor_prefix_str, &vendor_prefix); 249 250 paired_device->set_vendor_prefix(vendor_prefix); 251 } 252 253 switch (device->GetVendorIDSource()) { 254 case device::BluetoothDevice::VENDOR_ID_BLUETOOTH: 255 paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_BLUETOOTH); 256 break; 257 case device::BluetoothDevice::VENDOR_ID_USB: 258 paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_USB); 259 break; 260 default: 261 paired_device->set_vendor_id_source(PairedDevice::VENDOR_ID_UNKNOWN); 262 } 263 264 paired_device->set_vendor_id(device->GetVendorID()); 265 paired_device->set_product_id(device->GetProductID()); 266 paired_device->set_device_id(device->GetDeviceID()); 267 } 268 } 269 270 void ChromeOSMetricsProvider::UpdateMultiProfileUserCount( 271 metrics::SystemProfileProto* system_profile_proto) { 272 if (user_manager::UserManager::IsInitialized()) { 273 size_t user_count = 274 user_manager::UserManager::Get()->GetLoggedInUsers().size(); 275 276 // We invalidate the user count if it changed while the log was open. 277 if (registered_user_count_at_log_initialization_ && 278 user_count != user_count_at_log_initialization_) { 279 user_count = 0; 280 } 281 282 system_profile_proto->set_multi_profile_user_count(user_count); 283 } 284 } 285 286 void ChromeOSMetricsProvider::SetBluetoothAdapter( 287 scoped_refptr<device::BluetoothAdapter> adapter) { 288 adapter_ = adapter; 289 } 290