Commit 8c264f9f authored by xiyuan@chromium.org's avatar xiyuan@chromium.org

No Easy unlock if bluetooth is not available.

BUG=399067

Review URL: https://codereview.chromium.org/472733002

Cr-Commit-Position: refs/heads/master@{#290081}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290081 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b8d1c64
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h" #include "base/prefs/scoped_user_pref_update.h"
...@@ -23,6 +24,8 @@ ...@@ -23,6 +24,8 @@
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/common/one_shot_event.h" #include "extensions/common/one_shot_event.h"
#include "grit/browser_resources.h" #include "grit/browser_resources.h"
...@@ -58,8 +61,56 @@ EasyUnlockService* EasyUnlockService::Get(Profile* profile) { ...@@ -58,8 +61,56 @@ EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
return EasyUnlockServiceFactory::GetForProfile(profile); return EasyUnlockServiceFactory::GetForProfile(profile);
} }
class EasyUnlockService::BluetoothDetector
: public device::BluetoothAdapter::Observer {
public:
explicit BluetoothDetector(EasyUnlockService* service)
: service_(service),
weak_ptr_factory_(this) {
}
virtual ~BluetoothDetector() {
if (adapter_)
adapter_->RemoveObserver(this);
}
void Initialize() {
if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
return;
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothDetector::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
bool IsPresent() const {
return adapter_ && adapter_->IsPresent();
}
// device::BluetoothAdapter::Observer:
virtual void AdapterPresentChanged(device::BluetoothAdapter* adapter,
bool present) OVERRIDE {
service_->OnBluetoothAdapterPresentChanged();
}
private:
void OnAdapterInitialized(scoped_refptr<device::BluetoothAdapter> adapter) {
adapter_ = adapter;
adapter_->AddObserver(this);
service_->OnBluetoothAdapterPresentChanged();
}
// Owner of this class and should out-live this class.
EasyUnlockService* service_;
scoped_refptr<device::BluetoothAdapter> adapter_;
base::WeakPtrFactory<BluetoothDetector> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothDetector);
};
EasyUnlockService::EasyUnlockService(Profile* profile) EasyUnlockService::EasyUnlockService(Profile* profile)
: profile_(profile), : profile_(profile),
bluetooth_detector_(new BluetoothDetector(this)),
turn_off_flow_status_(IDLE), turn_off_flow_status_(IDLE),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
extensions::ExtensionSystem::Get(profile_)->ready().Post( extensions::ExtensionSystem::Get(profile_)->ready().Post(
...@@ -113,8 +164,14 @@ bool EasyUnlockService::IsAllowed() { ...@@ -113,8 +164,14 @@ bool EasyUnlockService::IsAllowed() {
if (!profile_->GetPrefs()->GetBoolean(prefs::kEasyUnlockAllowed)) if (!profile_->GetPrefs()->GetBoolean(prefs::kEasyUnlockAllowed))
return false; return false;
// It is only disabled when the trial exists and is in "Disable" group. // It is disabled when the trial exists and is in "Disable" group.
return base::FieldTrialList::FindFullName("EasyUnlock") != "Disable"; if (base::FieldTrialList::FindFullName("EasyUnlock") == "Disable")
return false;
if (!bluetooth_detector_->IsPresent())
return false;
return true;
#else #else
// TODO(xiyuan): Revisit when non-chromeos platforms are supported. // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
return false; return false;
...@@ -232,6 +289,8 @@ void EasyUnlockService::Initialize() { ...@@ -232,6 +289,8 @@ void EasyUnlockService::Initialize() {
prefs::kEasyUnlockAllowed, prefs::kEasyUnlockAllowed,
base::Bind(&EasyUnlockService::OnPrefsChanged, base::Unretained(this))); base::Bind(&EasyUnlockService::OnPrefsChanged, base::Unretained(this)));
OnPrefsChanged(); OnPrefsChanged();
bluetooth_detector_->Initialize();
} }
void EasyUnlockService::LoadApp() { void EasyUnlockService::LoadApp() {
...@@ -253,17 +312,20 @@ void EasyUnlockService::LoadApp() { ...@@ -253,17 +312,20 @@ void EasyUnlockService::LoadApp() {
#endif // !defined(NDEBUG) #endif // !defined(NDEBUG)
if (!easy_unlock_path.empty()) { if (!easy_unlock_path.empty()) {
GetComponentLoader(profile_) extensions::ComponentLoader* loader = GetComponentLoader(profile_);
->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path); if (!loader->Exists(extension_misc::kEasyUnlockAppId))
loader->Add(IDR_EASY_UNLOCK_MANIFEST, easy_unlock_path);
} }
#endif // defined(GOOGLE_CHROME_BUILD) #endif // defined(GOOGLE_CHROME_BUILD)
} }
void EasyUnlockService::UnloadApp() { void EasyUnlockService::UnloadApp() {
GetComponentLoader(profile_)->Remove(extension_misc::kEasyUnlockAppId); extensions::ComponentLoader* loader = GetComponentLoader(profile_);
if (loader->Exists(extension_misc::kEasyUnlockAppId))
loader->Remove(extension_misc::kEasyUnlockAppId);
} }
void EasyUnlockService::OnPrefsChanged() { void EasyUnlockService::UpdateAppState() {
if (IsAllowed()) { if (IsAllowed()) {
LoadApp(); LoadApp();
} else { } else {
...@@ -274,6 +336,14 @@ void EasyUnlockService::OnPrefsChanged() { ...@@ -274,6 +336,14 @@ void EasyUnlockService::OnPrefsChanged() {
} }
} }
void EasyUnlockService::OnPrefsChanged() {
UpdateAppState();
}
void EasyUnlockService::OnBluetoothAdapterPresentChanged() {
UpdateAppState();
}
void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) { void EasyUnlockService::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
turn_off_flow_status_ = status; turn_off_flow_status_ = status;
FOR_EACH_OBSERVER( FOR_EACH_OBSERVER(
......
...@@ -78,16 +78,36 @@ class EasyUnlockService : public KeyedService { ...@@ -78,16 +78,36 @@ class EasyUnlockService : public KeyedService {
} }
private: private:
// A class to detect whether a bluetooth adapter is present.
class BluetoothDetector;
// Initializes the service after ExtensionService is ready.
void Initialize(); void Initialize();
// Loads the Easy unlock component app.
void LoadApp(); void LoadApp();
// Unloads the Easy unlock component app.
void UnloadApp(); void UnloadApp();
// Checks whether Easy unlock should be running and updates app state.
void UpdateAppState();
// Callback when the controlling pref changes.
void OnPrefsChanged(); void OnPrefsChanged();
// Callback when Bluetooth adapter present state changes.
void OnBluetoothAdapterPresentChanged();
// Sets the new turn-off flow status.
void SetTurnOffFlowStatus(TurnOffFlowStatus status); void SetTurnOffFlowStatus(TurnOffFlowStatus status);
// Callback invoked when turn off flow has finished.
void OnTurnOffFlowFinished(bool success); void OnTurnOffFlowFinished(bool success);
Profile* profile_; Profile* profile_;
PrefChangeRegistrar registrar_; PrefChangeRegistrar registrar_;
scoped_ptr<BluetoothDetector> bluetooth_detector_;
// Created lazily in |GetScreenlockStateHandler|. // Created lazily in |GetScreenlockStateHandler|.
scoped_ptr<EasyUnlockScreenlockStateHandler> screenlock_state_handler_; scoped_ptr<EasyUnlockScreenlockStateHandler> screenlock_state_handler_;
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "components/policy/core/common/policy_types.h" #include "components/policy/core/common/policy_types.h"
#include "components/user_manager/user_manager.h" #include "components/user_manager/user_manager.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "policy/policy_constants.h" #include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
...@@ -31,6 +33,7 @@ using chromeos::LoginManagerTest; ...@@ -31,6 +33,7 @@ using chromeos::LoginManagerTest;
using chromeos::StartupUtils; using chromeos::StartupUtils;
using chromeos::UserAddingScreen; using chromeos::UserAddingScreen;
using user_manager::UserManager; using user_manager::UserManager;
using device::MockBluetoothAdapter;
using testing::_; using testing::_;
using testing::Return; using testing::Return;
...@@ -49,11 +52,27 @@ bool HasEasyUnlockAppForProfile(Profile* profile) { ...@@ -49,11 +52,27 @@ bool HasEasyUnlockAppForProfile(Profile* profile) {
} }
#endif #endif
void SetUpBluetoothMock(
scoped_refptr<testing::NiceMock<MockBluetoothAdapter> > mock_adapter,
bool is_present) {
device::BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter);
EXPECT_CALL(*mock_adapter, IsPresent())
.WillRepeatedly(testing::Return(is_present));
// These functions are called from ash system tray. They are speculations of
// why flaky gmock errors are seen on bots.
EXPECT_CALL(*mock_adapter, IsPowered())
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_adapter, GetDevices()).WillRepeatedly(
testing::Return(device::BluetoothAdapter::ConstDeviceList()));
}
} // namespace } // namespace
class EasyUnlockServiceTest : public InProcessBrowserTest { class EasyUnlockServiceTest : public InProcessBrowserTest {
public: public:
EasyUnlockServiceTest() {} EasyUnlockServiceTest() : is_bluetooth_adapter_present_(true) {}
virtual ~EasyUnlockServiceTest() {} virtual ~EasyUnlockServiceTest() {}
void SetEasyUnlockAllowedPolicy(bool allowed) { void SetEasyUnlockAllowedPolicy(bool allowed) {
...@@ -78,6 +97,9 @@ class EasyUnlockServiceTest : public InProcessBrowserTest { ...@@ -78,6 +97,9 @@ class EasyUnlockServiceTest : public InProcessBrowserTest {
EXPECT_CALL(provider_, IsInitializationComplete(_)) EXPECT_CALL(provider_, IsInitializationComplete(_))
.WillRepeatedly(Return(true)); .WillRepeatedly(Return(true));
policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
mock_adapter_ = new testing::NiceMock<MockBluetoothAdapter>();
SetUpBluetoothMock(mock_adapter_, is_bluetooth_adapter_present_);
} }
Profile* profile() const { return browser()->profile(); } Profile* profile() const { return browser()->profile(); }
...@@ -86,8 +108,14 @@ class EasyUnlockServiceTest : public InProcessBrowserTest { ...@@ -86,8 +108,14 @@ class EasyUnlockServiceTest : public InProcessBrowserTest {
return EasyUnlockService::Get(profile()); return EasyUnlockService::Get(profile());
} }
void set_is_bluetooth_adapter_present(bool is_present) {
is_bluetooth_adapter_present_ = is_present;
}
private: private:
policy::MockConfigurationPolicyProvider provider_; policy::MockConfigurationPolicyProvider provider_;
scoped_refptr<testing::NiceMock<MockBluetoothAdapter> > mock_adapter_;
bool is_bluetooth_adapter_present_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceTest); DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceTest);
}; };
...@@ -100,6 +128,28 @@ IN_PROC_BROWSER_TEST_F(EasyUnlockServiceTest, DefaultOn) { ...@@ -100,6 +128,28 @@ IN_PROC_BROWSER_TEST_F(EasyUnlockServiceTest, DefaultOn) {
#endif #endif
} }
class EasyUnlockServiceNoBluetoothTest : public EasyUnlockServiceTest {
public:
EasyUnlockServiceNoBluetoothTest() {}
virtual ~EasyUnlockServiceNoBluetoothTest() {}
// InProcessBrowserTest:
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
set_is_bluetooth_adapter_present(false);
EasyUnlockServiceTest::SetUpInProcessBrowserTestFixture();
}
private:
DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceNoBluetoothTest);
};
IN_PROC_BROWSER_TEST_F(EasyUnlockServiceNoBluetoothTest, NoService) {
EXPECT_FALSE(service()->IsAllowed());
#if defined(GOOGLE_CHROME_BUILD)
EXPECT_FALSE(HasEasyUnlockApp());
#endif
}
class EasyUnlockServiceFinchEnabledTest : public EasyUnlockServiceTest { class EasyUnlockServiceFinchEnabledTest : public EasyUnlockServiceTest {
public: public:
EasyUnlockServiceFinchEnabledTest() {} EasyUnlockServiceFinchEnabledTest() {}
...@@ -172,7 +222,16 @@ class EasyUnlockServiceMultiProfileTest : public LoginManagerTest { ...@@ -172,7 +222,16 @@ class EasyUnlockServiceMultiProfileTest : public LoginManagerTest {
EasyUnlockServiceMultiProfileTest() : LoginManagerTest(false) {} EasyUnlockServiceMultiProfileTest() : LoginManagerTest(false) {}
virtual ~EasyUnlockServiceMultiProfileTest() {} virtual ~EasyUnlockServiceMultiProfileTest() {}
// InProcessBrowserTest:
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
LoginManagerTest::SetUpInProcessBrowserTestFixture();
mock_adapter_ = new testing::NiceMock<MockBluetoothAdapter>();
SetUpBluetoothMock(mock_adapter_, true);
}
private: private:
scoped_refptr<testing::NiceMock<MockBluetoothAdapter> > mock_adapter_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceMultiProfileTest); DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceMultiProfileTest);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment