Commit 57c04be7 authored by hirono@chromium.org's avatar hirono@chromium.org

Add DeviceEventRouter class.

The class is extracted from EventRouter of Files.app.
It is going to manage device states and dispatch the device events.

BUG=360946, 396258
TEST=add unit tests

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

Cr-Commit-Position: refs/heads/master@{#291384}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291384 0039d316-1c4b-4281-b951-d872f2087c98
parent eb077e54
// 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 "base/bind.h"
#include "base/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/extensions/file_manager/device_event_router.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "content/public/browser/browser_thread.h"
namespace file_manager {
namespace {
namespace file_browser_private = extensions::api::file_browser_private;
using content::BrowserThread;
} // namespace
DeviceEventRouter::DeviceEventRouter()
: resume_time_delta_(base::TimeDelta::FromSeconds(5)),
startup_time_delta_(base::TimeDelta::FromSeconds(10)),
scan_time_delta_(base::TimeDelta::FromSeconds(5)),
is_starting_up_(false),
is_resuming_(false),
weak_factory_(this) {
}
DeviceEventRouter::DeviceEventRouter(base::TimeDelta overriding_time_delta)
: resume_time_delta_(overriding_time_delta),
startup_time_delta_(overriding_time_delta),
scan_time_delta_(overriding_time_delta),
is_starting_up_(false),
is_resuming_(false),
weak_factory_(this) {
}
DeviceEventRouter::~DeviceEventRouter() {
}
void DeviceEventRouter::Startup() {
is_starting_up_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&DeviceEventRouter::StartupDelayed,
weak_factory_.GetWeakPtr()),
startup_time_delta_);
}
void DeviceEventRouter::StartupDelayed() {
DCHECK(thread_checker_.CalledOnValidThread());
is_starting_up_ = false;
}
void DeviceEventRouter::OnDeviceAdded(const std::string& device_path) {
DCHECK(thread_checker_.CalledOnValidThread());
if (is_starting_up_ || is_resuming_) {
SetDeviceState(device_path, DEVICE_STATE_USUAL);
return;
}
if (IsExternalStorageDisabled()) {
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
device_path);
SetDeviceState(device_path, DEVICE_STATE_USUAL);
return;
}
SetDeviceState(device_path, DEVICE_SCANNED);
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&DeviceEventRouter::OnDeviceAddedDelayed,
weak_factory_.GetWeakPtr(),
device_path),
scan_time_delta_);
}
void DeviceEventRouter::OnDeviceAddedDelayed(const std::string& device_path) {
DCHECK(thread_checker_.CalledOnValidThread());
if (GetDeviceState(device_path) == DEVICE_SCANNED) {
// TODO(hirono): Rename DEVICE_EVENT_TYPE_ADDED with
// DEVICE_EVENT_TYPE_SCAN_STARTED.
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_ADDED, device_path);
SetDeviceState(device_path, DEVICE_SCANNED_AND_REPORTED);
}
}
void DeviceEventRouter::OnDeviceRemoved(const std::string& device_path) {
DCHECK(thread_checker_.CalledOnValidThread());
SetDeviceState(device_path, DEVICE_STATE_USUAL);
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_REMOVED, device_path);
}
void DeviceEventRouter::OnDiskAdded(
const chromeos::disks::DiskMountManager::Disk& disk,
bool mounting) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!mounting) {
// If the disk is not being mounted, mark the device scan cancelled.
const std::string& device_path = disk.system_path_prefix();
if (GetDeviceState(device_path) == DEVICE_SCANNED_AND_REPORTED) {
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
device_path);
}
SetDeviceState(device_path, DEVICE_STATE_USUAL);
}
}
void DeviceEventRouter::OnDiskRemoved(
const chromeos::disks::DiskMountManager::Disk& disk) {
DCHECK(thread_checker_.CalledOnValidThread());
if (is_resuming_ || is_starting_up_)
return;
const std::string& device_path = disk.system_path_prefix();
if (!disk.mount_path().empty() &&
GetDeviceState(device_path) != DEVICE_HARD_UNPLUGGED_AND_REPORTED) {
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
device_path);
SetDeviceState(device_path, DEVICE_HARD_UNPLUGGED_AND_REPORTED);
}
}
void DeviceEventRouter::OnVolumeMounted(chromeos::MountError error_code,
const VolumeInfo& volume_info,
bool is_remounting) {
DCHECK(thread_checker_.CalledOnValidThread());
const std::string& device_path =
volume_info.system_path_prefix.AsUTF8Unsafe();
SetDeviceState(device_path, DEVICE_STATE_USUAL);
}
void DeviceEventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
const VolumeInfo& volume_info) {
// Do nothing.
}
void DeviceEventRouter::OnFormatStarted(const std::string& device_path,
bool success) {
DCHECK(thread_checker_.CalledOnValidThread());
if (success) {
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
device_path);
} else {
OnDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
device_path);
}
}
void DeviceEventRouter::OnFormatCompleted(const std::string& device_path,
bool success) {
DCHECK(thread_checker_.CalledOnValidThread());
OnDeviceEvent(success ? file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS
: file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
device_path);
}
void DeviceEventRouter::OnHardUnplugged(const std::string& device_path) {
}
void DeviceEventRouter::SuspendImminent() {
DCHECK(thread_checker_.CalledOnValidThread());
is_resuming_ = true;
}
void DeviceEventRouter::SuspendDone(const base::TimeDelta& sleep_duration) {
DCHECK(thread_checker_.CalledOnValidThread());
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&DeviceEventRouter::SuspendDoneDelayed,
weak_factory_.GetWeakPtr()),
resume_time_delta_);
}
void DeviceEventRouter::SuspendDoneDelayed() {
DCHECK(thread_checker_.CalledOnValidThread());
is_resuming_ = false;
}
DeviceState DeviceEventRouter::GetDeviceState(
const std::string& device_path) const {
const std::map<std::string, DeviceState>::const_iterator it =
device_states_.find(device_path);
return it != device_states_.end() ? it->second : DEVICE_STATE_USUAL;
}
void DeviceEventRouter::SetDeviceState(const std::string& device_path,
DeviceState state) {
if (state != DEVICE_STATE_USUAL) {
device_states_[device_path] = state;
} else {
const std::map<std::string, DeviceState>::iterator it =
device_states_.find(device_path);
if (it != device_states_.end())
device_states_.erase(it);
}
}
} // namespace file_manager
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_DEVICE_EVENT_ROUTER_H_
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_DEVICE_EVENT_ROUTER_H_
#include <map>
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
#include "chrome/common/extensions/api/file_browser_private.h"
#include "chromeos/dbus/power_manager_client.h"
namespace file_manager {
enum DeviceState {
// Device is not being scanned and is not hard unplugged.
DEVICE_STATE_USUAL,
DEVICE_SCANNED,
DEVICE_SCANNED_AND_REPORTED,
DEVICE_HARD_UNPLUGGED,
DEVICE_HARD_UNPLUGGED_AND_REPORTED
};
// Event router for device events.
class DeviceEventRouter : public VolumeManagerObserver,
public chromeos::PowerManagerClient::Observer {
public:
DeviceEventRouter();
// |overriding_time_delta| overrides time delta of delayed tasks for testing
// |so that the tasks are executed by RunLoop::RunUntilIdle.
explicit DeviceEventRouter(base::TimeDelta overriding_time_delta);
virtual ~DeviceEventRouter();
// Turns the startup flag on, and then turns it off after few seconds.
void Startup();
// VolumeManagerObserver overrides.
virtual void OnDiskAdded(const chromeos::disks::DiskMountManager::Disk& disk,
bool mounting) OVERRIDE;
virtual void OnDiskRemoved(
const chromeos::disks::DiskMountManager::Disk& disk) OVERRIDE;
virtual void OnDeviceAdded(const std::string& device_path) OVERRIDE;
virtual void OnDeviceRemoved(const std::string& device_path) OVERRIDE;
virtual void OnVolumeMounted(chromeos::MountError error_code,
const VolumeInfo& volume_info,
bool is_remounting) OVERRIDE;
virtual void OnVolumeUnmounted(chromeos::MountError error_code,
const VolumeInfo& volume_info) OVERRIDE;
virtual void OnFormatStarted(const std::string& device_path,
bool success) OVERRIDE;
virtual void OnFormatCompleted(const std::string& device_path,
bool success) OVERRIDE;
// TODO(hirono): Remove the method from VolumeManagerObserver.
virtual void OnHardUnplugged(const std::string& device_path) OVERRIDE;
// PowerManagerClient::Observer overrides.
virtual void SuspendImminent() OVERRIDE;
virtual void SuspendDone(const base::TimeDelta& sleep_duration) OVERRIDE;
protected:
// Handles a device event containing |type| and |device_path|.
virtual void OnDeviceEvent(
extensions::api::file_browser_private::DeviceEventType type,
const std::string& device_path) = 0;
// Returns external storage is disabled or not.
virtual bool IsExternalStorageDisabled() = 0;
private:
void StartupDelayed();
void OnDeviceAddedDelayed(const std::string& device_path);
void SuspendDoneDelayed();
// Obtains device state of the device having |device_path|.
DeviceState GetDeviceState(const std::string& device_path) const;
// Sets device state to the device having |device_path|.
void SetDeviceState(const std::string& device_path, DeviceState state);
// Whther to use zero time delta for testing or not.
const base::TimeDelta resume_time_delta_;
const base::TimeDelta startup_time_delta_;
const base::TimeDelta scan_time_delta_;
// Whether the profile is starting up or not.
bool is_starting_up_;
// Whether the system is resuming or not.
bool is_resuming_;
// Map of device path and device state.
std::map<std::string, DeviceState> device_states_;
// Thread checker.
base::ThreadChecker thread_checker_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate the weak pointers before any other members are destroyed.
base::WeakPtrFactory<DeviceEventRouter> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceEventRouter);
};
} // namespace file_manager
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_DEVICE_EVENT_ROUTER_H_
// 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/chromeos/extensions/file_manager/device_event_router.h"
#include <string>
#include <vector>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace file_manager {
namespace {
namespace file_browser_private = extensions::api::file_browser_private;
typedef chromeos::disks::DiskMountManager::Disk Disk;
const char kTestDevicePath[] = "/device/test";
struct DeviceEvent {
extensions::api::file_browser_private::DeviceEventType type;
std::string device_path;
};
// DeviceEventRouter implementation for testing.
class DeviceEventRouterImpl : public DeviceEventRouter {
public:
DeviceEventRouterImpl()
: DeviceEventRouter(base::TimeDelta::FromSeconds(0)),
external_storage_disabled(false) {}
virtual ~DeviceEventRouterImpl() {}
// DeviceEventRouter overrides.
virtual void OnDeviceEvent(file_browser_private::DeviceEventType type,
const std::string& device_path) OVERRIDE {
DeviceEvent event;
event.type = type;
event.device_path = device_path;
events.push_back(event);
}
// DeviceEventRouter overrides.
virtual bool IsExternalStorageDisabled() OVERRIDE {
return external_storage_disabled;
}
// List of dispatched events.
std::vector<DeviceEvent> events;
// Flag returned by |IsExternalStorageDisabled|.
bool external_storage_disabled;
private:
DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl);
};
} // namespace
class DeviceEventRouterTest : public testing::Test {
protected:
virtual void SetUp() OVERRIDE {
device_event_router.reset(new DeviceEventRouterImpl());
}
// Creates a disk instance with |device_path| and |mount_path| for testing.
Disk CreateTestDisk(const std::string& device_path,
const std::string& mount_path) {
return Disk(device_path,
mount_path,
"",
"",
"",
"",
"",
"",
"",
"",
"",
device_path,
chromeos::DEVICE_TYPE_UNKNOWN,
0,
false,
false,
false,
false,
false,
false);
}
// Creates a volume info instance with |device_path| and |mount_path| for
// testing.
VolumeInfo CreateTestVolumeInfo(const std::string& device_path,
const std::string& mount_path) {
VolumeInfo volume_info;
volume_info.system_path_prefix = base::FilePath(device_path);
volume_info.mount_path = base::FilePath(mount_path);
return volume_info;
}
scoped_ptr<DeviceEventRouterImpl> device_event_router;
private:
base::MessageLoop message_loop_;
};
TEST_F(DeviceEventRouterTest, AddAndRemoveDevice) {
const Disk disk1 = CreateTestDisk("/device/test", "/mount/path1");
const Disk disk1_unmounted = CreateTestDisk("/device/test", "");
const VolumeInfo volumeInfo =
CreateTestVolumeInfo("/device/test", "/mount/path1");
device_event_router->OnDeviceAdded("/device/test");
device_event_router->OnDiskAdded(disk1, true);
device_event_router->OnVolumeMounted(
chromeos::MOUNT_ERROR_NONE, volumeInfo, false);
device_event_router->OnVolumeUnmounted(chromeos::MOUNT_ERROR_NONE,
volumeInfo);
device_event_router->OnDiskRemoved(disk1_unmounted);
device_event_router->OnDeviceRemoved("/device/test");
ASSERT_EQ(1u, device_event_router->events.size());
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
device_event_router->events[0].type);
EXPECT_EQ("/device/test", device_event_router->events[0].device_path);
}
TEST_F(DeviceEventRouterTest, DeviceScan) {
const Disk disk = CreateTestDisk("/device/test", "/mount/path1");
const Disk disk_unmounted = CreateTestDisk("/device/test", "");
const VolumeInfo volumeInfo =
CreateTestVolumeInfo("/device/test", "/mount/path1");
device_event_router->OnDeviceAdded("/device/test");
base::RunLoop().RunUntilIdle();
device_event_router->OnDiskAdded(disk, true);
device_event_router->OnVolumeMounted(
chromeos::MOUNT_ERROR_NONE, volumeInfo, false);
device_event_router->OnVolumeUnmounted(chromeos::MOUNT_ERROR_NONE,
volumeInfo);
device_event_router->OnDiskRemoved(disk_unmounted);
device_event_router->OnDeviceRemoved("/device/test");
ASSERT_EQ(2u, device_event_router->events.size());
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_ADDED,
device_event_router->events[0].type);
EXPECT_EQ("/device/test", device_event_router->events[0].device_path);
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
device_event_router->events[1].type);
EXPECT_EQ("/device/test", device_event_router->events[1].device_path);
}
TEST_F(DeviceEventRouterTest, DeviceScanCancelled) {
const Disk disk = CreateTestDisk("/device/test", "/mount/path1");
const Disk disk_unmounted = CreateTestDisk("/device/test", "");
const VolumeInfo volumeInfo =
CreateTestVolumeInfo("/device/test", "/mount/path1");
device_event_router->OnDeviceAdded("/device/test");
base::RunLoop().RunUntilIdle();
device_event_router->OnDiskAdded(disk, false);
device_event_router->OnDiskRemoved(disk_unmounted);
device_event_router->OnDeviceRemoved("/device/test");
ASSERT_EQ(3u, device_event_router->events.size());
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_ADDED,
device_event_router->events[0].type);
EXPECT_EQ("/device/test", device_event_router->events[0].device_path);
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
device_event_router->events[1].type);
EXPECT_EQ("/device/test", device_event_router->events[1].device_path);
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
device_event_router->events[2].type);
EXPECT_EQ("/device/test", device_event_router->events[2].device_path);
}
TEST_F(DeviceEventRouterTest, HardUnplugged) {
const Disk disk1 = CreateTestDisk("/device/test", "/mount/path1");
const Disk disk2 = CreateTestDisk("/device/test", "/mount/path2");
device_event_router->OnDeviceAdded("/device/test");
device_event_router->OnDiskAdded(disk1, true);
device_event_router->OnDiskAdded(disk2, true);
device_event_router->OnDiskRemoved(disk1);
device_event_router->OnDiskRemoved(disk2);
device_event_router->OnDeviceRemoved(kTestDevicePath);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, device_event_router->events.size());
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
device_event_router->events[0].type);
EXPECT_EQ("/device/test", device_event_router->events[0].device_path);
EXPECT_EQ(file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
device_event_router->events[1].type);
EXPECT_EQ("/device/test", device_event_router->events[1].device_path);
}
} // namespace file_manager
...@@ -1037,6 +1037,8 @@ ...@@ -1037,6 +1037,8 @@
# Only extension API implementations should go here. # Only extension API implementations should go here.
'browser/chromeos/extensions/echo_private_api.cc', 'browser/chromeos/extensions/echo_private_api.cc',
'browser/chromeos/extensions/echo_private_api.h', 'browser/chromeos/extensions/echo_private_api.h',
'browser/chromeos/extensions/file_manager/device_event_router.cc',
'browser/chromeos/extensions/file_manager/device_event_router.h',
'browser/chromeos/extensions/file_manager/event_router.cc', 'browser/chromeos/extensions/file_manager/event_router.cc',
'browser/chromeos/extensions/file_manager/event_router.h', 'browser/chromeos/extensions/file_manager/event_router.h',
'browser/chromeos/extensions/file_manager/file_browser_handler_api.cc', 'browser/chromeos/extensions/file_manager/file_browser_handler_api.cc',
......
...@@ -684,6 +684,7 @@ ...@@ -684,6 +684,7 @@
'browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc', 'browser/chromeos/extensions/device_local_account_external_policy_loader_unittest.cc',
'browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc', 'browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc',
'browser/chromeos/extensions/external_cache_unittest.cc', 'browser/chromeos/extensions/external_cache_unittest.cc',
'browser/chromeos/extensions/file_manager/device_event_router_unittest.cc',
'browser/chromeos/extensions/wallpaper_private_api_unittest.cc', 'browser/chromeos/extensions/wallpaper_private_api_unittest.cc',
'browser/chromeos/external_metrics_unittest.cc', 'browser/chromeos/external_metrics_unittest.cc',
'browser/chromeos/file_manager/fake_disk_mount_manager.cc', 'browser/chromeos/file_manager/fake_disk_mount_manager.cc',
......
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