|  | // 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 <string> | 
|  |  | 
|  | #include "base/macros.h" | 
|  | #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" | 
|  | #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" | 
|  | #include "chrome/browser/metrics/chromeos_metrics_provider.h" | 
|  | #include "chromeos/dbus/dbus_thread_manager.h" | 
|  | #include "chromeos/dbus/power_manager_client.h" | 
|  | #include "chromeos/login/login_state.h" | 
|  | #include "components/metrics/proto/system_profile.pb.h" | 
|  | #include "components/user_manager/user_manager.h" | 
|  | #include "content/public/test/test_browser_thread_bundle.h" | 
|  | #include "content/public/test/test_utils.h" | 
|  | #include "device/bluetooth/dbus/bluez_dbus_manager.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_device_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_gatt_service_client.h" | 
|  | #include "device/bluetooth/dbus/fake_bluetooth_input_client.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #if defined(USE_X11) | 
|  | #include "ui/events/devices/x11/device_data_manager_x11.h" | 
|  | #endif | 
|  |  | 
|  | using bluez::BluetoothAdapterClient; | 
|  | using bluez::BluetoothAgentManagerClient; | 
|  | using bluez::BluetoothDeviceClient; | 
|  | using bluez::BluetoothGattCharacteristicClient; | 
|  | using bluez::BluetoothGattDescriptorClient; | 
|  | using bluez::BluetoothGattServiceClient; | 
|  | using bluez::BluetoothInputClient; | 
|  | using bluez::BluezDBusManager; | 
|  | using bluez::BluezDBusManagerSetter; | 
|  | using bluez::FakeBluetoothAdapterClient; | 
|  | using bluez::FakeBluetoothAgentManagerClient; | 
|  | using bluez::FakeBluetoothDeviceClient; | 
|  | using bluez::FakeBluetoothGattCharacteristicClient; | 
|  | using bluez::FakeBluetoothGattDescriptorClient; | 
|  | using bluez::FakeBluetoothGattServiceClient; | 
|  | using bluez::FakeBluetoothInputClient; | 
|  | using chromeos::DBusThreadManager; | 
|  | using chromeos::DBusThreadManagerSetter; | 
|  | using chromeos::PowerManagerClient; | 
|  | using chromeos::STUB_DBUS_CLIENT_IMPLEMENTATION; | 
|  |  | 
|  | class ChromeOSMetricsProviderTest : public testing::Test { | 
|  | public: | 
|  | ChromeOSMetricsProviderTest() {} | 
|  |  | 
|  | protected: | 
|  | void SetUp() override { | 
|  | #if defined(USE_X11) | 
|  | ui::DeviceDataManagerX11::CreateInstance(); | 
|  | #endif | 
|  |  | 
|  | // Set up the fake Bluetooth environment, | 
|  | scoped_ptr<BluezDBusManagerSetter> bluez_dbus_setter = | 
|  | BluezDBusManager::GetSetterForTesting(); | 
|  | bluez_dbus_setter->SetBluetoothAdapterClient( | 
|  | scoped_ptr<BluetoothAdapterClient>(new FakeBluetoothAdapterClient)); | 
|  | bluez_dbus_setter->SetBluetoothDeviceClient( | 
|  | scoped_ptr<BluetoothDeviceClient>(new FakeBluetoothDeviceClient)); | 
|  | bluez_dbus_setter->SetBluetoothGattCharacteristicClient( | 
|  | scoped_ptr<BluetoothGattCharacteristicClient>( | 
|  | new FakeBluetoothGattCharacteristicClient)); | 
|  | bluez_dbus_setter->SetBluetoothGattDescriptorClient( | 
|  | scoped_ptr<BluetoothGattDescriptorClient>( | 
|  | new FakeBluetoothGattDescriptorClient)); | 
|  | bluez_dbus_setter->SetBluetoothGattServiceClient( | 
|  | scoped_ptr<BluetoothGattServiceClient>( | 
|  | new FakeBluetoothGattServiceClient)); | 
|  | bluez_dbus_setter->SetBluetoothInputClient( | 
|  | scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient)); | 
|  | bluez_dbus_setter->SetBluetoothAgentManagerClient( | 
|  | scoped_ptr<BluetoothAgentManagerClient>( | 
|  | new FakeBluetoothAgentManagerClient)); | 
|  |  | 
|  | // Set up a PowerManagerClient instance for PerfProvider. | 
|  | scoped_ptr<DBusThreadManagerSetter> dbus_setter = | 
|  | DBusThreadManager::GetSetterForTesting(); | 
|  | dbus_setter->SetPowerManagerClient( | 
|  | scoped_ptr<PowerManagerClient>( | 
|  | PowerManagerClient::Create(STUB_DBUS_CLIENT_IMPLEMENTATION))); | 
|  |  | 
|  | // Grab pointers to members of the thread manager for easier testing. | 
|  | fake_bluetooth_adapter_client_ = static_cast<FakeBluetoothAdapterClient*>( | 
|  | BluezDBusManager::Get()->GetBluetoothAdapterClient()); | 
|  | fake_bluetooth_device_client_ = static_cast<FakeBluetoothDeviceClient*>( | 
|  | BluezDBusManager::Get()->GetBluetoothDeviceClient()); | 
|  |  | 
|  | // Initialize the login state trackers. | 
|  | if (!chromeos::LoginState::IsInitialized()) | 
|  | chromeos::LoginState::Initialize(); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | // Destroy the login state tracker if it was initialized. | 
|  | chromeos::LoginState::Shutdown(); | 
|  |  | 
|  | DBusThreadManager::Shutdown(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_; | 
|  | FakeBluetoothDeviceClient* fake_bluetooth_device_client_; | 
|  |  | 
|  | private: | 
|  | content::TestBrowserThreadBundle thread_bundle_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ChromeOSMetricsProviderTest); | 
|  | }; | 
|  |  | 
|  | TEST_F(ChromeOSMetricsProviderTest, MultiProfileUserCount) { | 
|  | const AccountId account_id1(AccountId::FromUserEmail("user1@example.com")); | 
|  | const AccountId account_id2(AccountId::FromUserEmail("user2@example.com")); | 
|  | const AccountId account_id3(AccountId::FromUserEmail("user3@example.com")); | 
|  |  | 
|  | // |scoped_enabler| takes over the lifetime of |user_manager|. | 
|  | chromeos::FakeChromeUserManager* user_manager = | 
|  | new chromeos::FakeChromeUserManager(); | 
|  | chromeos::ScopedUserManagerEnabler scoped_enabler(user_manager); | 
|  | user_manager->AddKioskAppUser(account_id1); | 
|  | user_manager->AddKioskAppUser(account_id2); | 
|  | user_manager->AddKioskAppUser(account_id3); | 
|  |  | 
|  | user_manager->LoginUser(account_id1); | 
|  | user_manager->LoginUser(account_id3); | 
|  |  | 
|  | ChromeOSMetricsProvider provider; | 
|  | provider.OnDidCreateMetricsLog(); | 
|  | metrics::SystemProfileProto system_profile; | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  | EXPECT_EQ(2u, system_profile.multi_profile_user_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(ChromeOSMetricsProviderTest, MultiProfileCountInvalidated) { | 
|  | const AccountId account_id1(AccountId::FromUserEmail("user1@example.com")); | 
|  | const AccountId account_id2(AccountId::FromUserEmail("user2@example.com")); | 
|  | const AccountId account_id3(AccountId::FromUserEmail("user3@example.com")); | 
|  |  | 
|  | // |scoped_enabler| takes over the lifetime of |user_manager|. | 
|  | chromeos::FakeChromeUserManager* user_manager = | 
|  | new chromeos::FakeChromeUserManager(); | 
|  | chromeos::ScopedUserManagerEnabler scoped_enabler(user_manager); | 
|  | user_manager->AddKioskAppUser(account_id1); | 
|  | user_manager->AddKioskAppUser(account_id2); | 
|  | user_manager->AddKioskAppUser(account_id3); | 
|  |  | 
|  | user_manager->LoginUser(account_id1); | 
|  |  | 
|  | ChromeOSMetricsProvider provider; | 
|  | provider.OnDidCreateMetricsLog(); | 
|  |  | 
|  | metrics::SystemProfileProto system_profile; | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  | EXPECT_EQ(1u, system_profile.multi_profile_user_count()); | 
|  |  | 
|  | user_manager->LoginUser(account_id2); | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  | EXPECT_EQ(0u, system_profile.multi_profile_user_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(ChromeOSMetricsProviderTest, BluetoothHardwareDisabled) { | 
|  | ChromeOSMetricsProvider provider; | 
|  | provider.OnDidCreateMetricsLog(); | 
|  | metrics::SystemProfileProto system_profile; | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  |  | 
|  | EXPECT_TRUE(system_profile.has_hardware()); | 
|  | EXPECT_TRUE(system_profile.hardware().has_bluetooth()); | 
|  |  | 
|  | EXPECT_TRUE(system_profile.hardware().bluetooth().is_present()); | 
|  | EXPECT_FALSE(system_profile.hardware().bluetooth().is_enabled()); | 
|  | } | 
|  |  | 
|  | TEST_F(ChromeOSMetricsProviderTest, BluetoothHardwareEnabled) { | 
|  | FakeBluetoothAdapterClient::Properties* properties = | 
|  | fake_bluetooth_adapter_client_->GetProperties( | 
|  | dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); | 
|  | properties->powered.ReplaceValue(true); | 
|  |  | 
|  | ChromeOSMetricsProvider provider; | 
|  | metrics::SystemProfileProto system_profile; | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  |  | 
|  | EXPECT_TRUE(system_profile.has_hardware()); | 
|  | EXPECT_TRUE(system_profile.hardware().has_bluetooth()); | 
|  |  | 
|  | EXPECT_TRUE(system_profile.hardware().bluetooth().is_present()); | 
|  | EXPECT_TRUE(system_profile.hardware().bluetooth().is_enabled()); | 
|  | } | 
|  |  | 
|  | TEST_F(ChromeOSMetricsProviderTest, BluetoothPairedDevices) { | 
|  | // The fake bluetooth adapter class already claims to be paired with two | 
|  | // device when initialized. Add a third and fourth fake device to it so we | 
|  | // can test the cases where a device is not paired (LE device, generally) | 
|  | // and a device that does not have Device ID information. | 
|  | fake_bluetooth_device_client_->CreateDevice( | 
|  | dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), | 
|  | dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath)); | 
|  |  | 
|  | fake_bluetooth_device_client_->CreateDevice( | 
|  | dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), | 
|  | dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath)); | 
|  |  | 
|  | FakeBluetoothDeviceClient::Properties* properties = | 
|  | fake_bluetooth_device_client_->GetProperties( | 
|  | dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath)); | 
|  | properties->paired.ReplaceValue(true); | 
|  |  | 
|  | ChromeOSMetricsProvider provider; | 
|  | provider.OnDidCreateMetricsLog(); | 
|  | metrics::SystemProfileProto system_profile; | 
|  | provider.ProvideSystemProfileMetrics(&system_profile); | 
|  |  | 
|  | ASSERT_TRUE(system_profile.has_hardware()); | 
|  | ASSERT_TRUE(system_profile.hardware().has_bluetooth()); | 
|  |  | 
|  | // Only three of the devices should appear. | 
|  | EXPECT_EQ(3, system_profile.hardware().bluetooth().paired_device_size()); | 
|  |  | 
|  | typedef metrics::SystemProfileProto::Hardware::Bluetooth::PairedDevice | 
|  | PairedDevice; | 
|  | // As BluetoothAdapter keeps the device list without ordering, | 
|  | // it's not appropriate to use fixed positional indices to index into the | 
|  | // system_profile.hardware().bluetooth().paired_device list. | 
|  | // Instead, directly find the two devices we're interested in. | 
|  | PairedDevice device1; | 
|  | PairedDevice device2; | 
|  | for (int i = 0; | 
|  | i < system_profile.hardware().bluetooth().paired_device_size(); ++i) { | 
|  | const PairedDevice& device = | 
|  | system_profile.hardware().bluetooth().paired_device(i); | 
|  | if (device.bluetooth_class() == | 
|  | FakeBluetoothDeviceClient::kPairedDeviceClass && | 
|  | device.vendor_prefix() == 0x001122U) { | 
|  | // Found the Paired Device object. | 
|  | device1 = device; | 
|  | } else if (device.bluetooth_class() == | 
|  | FakeBluetoothDeviceClient::kConfirmPasskeyClass) { | 
|  | // Found the Confirm Passkey object. | 
|  | device2 = device; | 
|  | } | 
|  | } | 
|  |  | 
|  | // The Paired Device object, complete with parsed Device ID information. | 
|  | EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceClass, | 
|  | device1.bluetooth_class()); | 
|  | EXPECT_EQ(PairedDevice::DEVICE_COMPUTER, device1.type()); | 
|  | EXPECT_EQ(0x001122U, device1.vendor_prefix()); | 
|  | EXPECT_EQ(PairedDevice::VENDOR_ID_USB, device1.vendor_id_source()); | 
|  | EXPECT_EQ(0x05ACU, device1.vendor_id()); | 
|  | EXPECT_EQ(0x030DU, device1.product_id()); | 
|  | EXPECT_EQ(0x0306U, device1.device_id()); | 
|  |  | 
|  | // The Confirm Passkey object, this has no Device ID information. | 
|  | EXPECT_EQ(FakeBluetoothDeviceClient::kConfirmPasskeyClass, | 
|  | device2.bluetooth_class()); | 
|  | EXPECT_EQ(PairedDevice::DEVICE_PHONE, device2.type()); | 
|  | EXPECT_EQ(0x207D74U, device2.vendor_prefix()); | 
|  | EXPECT_EQ(PairedDevice::VENDOR_ID_UNKNOWN, device2.vendor_id_source()); | 
|  | } |