Commit bee0df31 authored by kinaba@chromium.org's avatar kinaba@chromium.org

Mount MTP devices in Chrome OS Files.app.

This CL
* Adds new filesystem type kFileSystemTypeDeviceMediaAsFileStorage,
which is handled by chromeos::FileSystemBackend.
It uses DeviceMediaAsyncFileUtil, so the implementation is
shared with MTP support in mediaGalleries API (MediaFileSystemBackend).

We need different types since we need different handlings.
(Permission management for file handler extensions, unlimited CopyOrMoveValidator
for allowing non-media files, etc.)

* Adds monitoring by storage_monitor::RemovableStorageObserver in
file_manager::VolumeManager so that MTP device is detected and
mounted to the file manager.

BUG=363960

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266458 0039d316-1c4b-4281-b951-d872f2087c98
parent a934f4d5
......@@ -166,6 +166,7 @@
#include "chrome/browser/chromeos/drive/fileapi/file_system_backend_delegate.h"
#include "chrome/browser/chromeos/file_system_provider/fileapi/backend_delegate.h"
#include "chrome/browser/chromeos/fileapi/file_system_backend.h"
#include "chrome/browser/chromeos/fileapi/mtp_file_system_backend_delegate.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/system/input_device_settings.h"
......@@ -2498,6 +2499,7 @@ void ChromeContentBrowserClient::GetAdditionalFileSystemBackends(
chromeos::FileSystemBackend* backend = new chromeos::FileSystemBackend(
new drive::FileSystemBackendDelegate,
new chromeos::file_system_provider::BackendDelegate,
new chromeos::MTPFileSystemBackendDelegate(storage_partition_path),
browser_context->GetSpecialStoragePolicy(),
external_mount_points,
fileapi::ExternalMountPoints::GetSystemInstance());
......
......@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/drive/drive_integration_service.h"
#include "chrome/browser/chromeos/drive/file_errors.h"
#include "chrome/browser/chromeos/drive/file_system_interface.h"
......@@ -23,11 +24,14 @@
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/local_discovery/storage/privet_filesystem_constants.h"
#include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cros_disks_client.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/storage_monitor/storage_monitor.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "webkit/browser/fileapi/external_mount_points.h"
......@@ -38,6 +42,8 @@ namespace {
// A named constant to be passed to the |is_remounting| parameter.
const bool kNotRemounting = false;
const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
// Registers |path| as the "Downloads" folder to the FileSystem API backend.
// If another folder is already mounted. It revokes and overrides the old one.
bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
......@@ -215,6 +221,13 @@ VolumeInfo CreateProvidedFileSystemVolumeInfo(
return volume_info;
}
std::string GetMountPointNameForMediaStorage(
const storage_monitor::StorageInfo& info) {
std::string name(kFileManagerMTPMountNamePrefix);
name += info.device_id();
return name;
}
} // namespace
VolumeInfo::VolumeInfo()
......@@ -371,6 +384,15 @@ void VolumeManager::Initialize() {
weak_ptr_factory_.GetWeakPtr())));
privet_volume_lister_->Start();
}
// Subscribe to storage monitor for MTP notifications.
if (CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kEnableFileManagerMTP) &&
storage_monitor::StorageMonitor::GetInstance()) {
storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
base::Bind(&VolumeManager::OnStorageMonitorInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
}
void VolumeManager::Shutdown() {
......@@ -378,6 +400,8 @@ void VolumeManager::Shutdown() {
pref_change_registrar_.RemoveAll();
disk_mount_manager_->RemoveObserver(this);
if (storage_monitor::StorageMonitor::GetInstance())
storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
if (drive_integration_service_)
drive_integration_service_->RemoveObserver(this);
......@@ -684,6 +708,69 @@ void VolumeManager::OnPrivetVolumesAvailable(
}
}
void VolumeManager::OnRemovableStorageAttached(
const storage_monitor::StorageInfo& info) {
if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
return;
const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location());
const std::string fsid = GetMountPointNameForMediaStorage(info);
const std::string name = base::UTF16ToUTF8(info.GetDisplayName(false));
bool result =
fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
fsid, fileapi::kFileSystemTypeDeviceMediaAsFileStorage,
fileapi::FileSystemMountOption(), path);
DCHECK(result);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, base::Bind(
&MTPDeviceMapService::RegisterMTPFileSystem,
base::Unretained(MTPDeviceMapService::GetInstance()),
info.location(), fsid));
VolumeInfo volume_info;
volume_info.type = VOLUME_TYPE_MTP;
volume_info.mount_path = path;
volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
volume_info.is_parent = true;
volume_info.is_read_only = true;
volume_info.volume_id = "mtp:" + name;
volume_info.source_path = path;
volume_info.device_type = chromeos::DEVICE_TYPE_MOBILE;
DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
}
void VolumeManager::OnRemovableStorageDetached(
const storage_monitor::StorageInfo& info) {
if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
return;
for (std::map<std::string, VolumeInfo>::iterator it =
mounted_volumes_.begin(); it != mounted_volumes_.end(); ++it) {
if (it->second.source_path.value() == info.location()) {
DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, VolumeInfo(it->second));
const std::string fsid = GetMountPointNameForMediaStorage(info);
fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
fsid);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, base::Bind(
&MTPDeviceMapService::RevokeMTPFileSystem,
base::Unretained(MTPDeviceMapService::GetInstance()),
fsid));
return;
}
}
}
void VolumeManager::OnStorageMonitorInitialized() {
std::vector<storage_monitor::StorageInfo> storages =
storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
for (size_t i = 0; i < storages.size(); ++i)
OnRemovableStorageAttached(storages[i]);
storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
}
void VolumeManager::DoMountEvent(chromeos::MountError error_code,
const VolumeInfo& volume_info,
bool is_remounting) {
......
......@@ -23,6 +23,7 @@
#include "chromeos/dbus/cros_disks_client.h"
#include "chromeos/disks/disk_mount_manager.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/storage_monitor/removable_storage_observer.h"
class Profile;
......@@ -107,7 +108,8 @@ struct VolumeInfo {
class VolumeManager : public KeyedService,
public drive::DriveIntegrationServiceObserver,
public chromeos::disks::DiskMountManager::Observer,
public chromeos::file_system_provider::Observer {
public chromeos::file_system_provider::Observer,
public storage_monitor::RemovableStorageObserver {
public:
VolumeManager(
Profile* profile,
......@@ -184,7 +186,14 @@ class VolumeManager : public KeyedService,
// Called on change to kExternalStorageDisabled pref.
void OnExternalStorageDisabledChanged();
// RemovableStorageObserver overrides.
virtual void OnRemovableStorageAttached(
const storage_monitor::StorageInfo& info) OVERRIDE;
virtual void OnRemovableStorageDetached(
const storage_monitor::StorageInfo& info) OVERRIDE;
private:
void OnStorageMonitorInitialized();
void OnPrivetVolumesAvailable(
const local_discovery::PrivetVolumeLister::VolumeList& volumes);
void DoMountEvent(chromeos::MountError error_code,
......
......@@ -9,6 +9,7 @@
#include "base/strings/stringprintf.h"
#include "chrome/browser/chromeos/fileapi/file_access_permissions.h"
#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
#include "chromeos/dbus/cros_disks_client.h"
#include "webkit/browser/blob/file_stream_reader.h"
#include "webkit/browser/fileapi/async_file_util.h"
......@@ -28,12 +29,14 @@ bool FileSystemBackend::CanHandleURL(const fileapi::FileSystemURL& url) {
return url.type() == fileapi::kFileSystemTypeNativeLocal ||
url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal ||
url.type() == fileapi::kFileSystemTypeDrive ||
url.type() == fileapi::kFileSystemTypeProvided;
url.type() == fileapi::kFileSystemTypeProvided ||
url.type() == fileapi::kFileSystemTypeDeviceMediaAsFileStorage;
}
FileSystemBackend::FileSystemBackend(
FileSystemBackendDelegate* drive_delegate,
FileSystemBackendDelegate* file_system_provider_delegate,
FileSystemBackendDelegate* mtp_delegate,
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,
scoped_refptr<fileapi::ExternalMountPoints> mount_points,
fileapi::ExternalMountPoints* system_mount_points)
......@@ -42,6 +45,7 @@ FileSystemBackend::FileSystemBackend(
local_file_util_(fileapi::AsyncFileUtil::CreateForLocalFileSystem()),
drive_delegate_(drive_delegate),
file_system_provider_delegate_(file_system_provider_delegate),
mtp_delegate_(mtp_delegate),
mount_points_(mount_points),
system_mount_points_(system_mount_points) {}
......@@ -76,6 +80,7 @@ bool FileSystemBackend::CanHandleType(fileapi::FileSystemType type) const {
case fileapi::kFileSystemTypeRestrictedNativeLocal:
case fileapi::kFileSystemTypeNativeLocal:
case fileapi::kFileSystemTypeNativeForPlatformApp:
case fileapi::kFileSystemTypeDeviceMediaAsFileStorage:
return true;
default:
return false;
......@@ -225,6 +230,8 @@ fileapi::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil(
case fileapi::kFileSystemTypeNativeLocal:
case fileapi::kFileSystemTypeRestrictedNativeLocal:
return local_file_util_.get();
case fileapi::kFileSystemTypeDeviceMediaAsFileStorage:
return mtp_delegate_->GetAsyncFileUtil(type);
default:
NOTREACHED();
}
......@@ -250,6 +257,14 @@ fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation(
return NULL;
}
if (url.type() == fileapi::kFileSystemTypeDeviceMediaAsFileStorage) {
// MTP file operations run on MediaTaskRunner.
return fileapi::FileSystemOperation::Create(
url, context,
make_scoped_ptr(new fileapi::FileSystemOperationContext(
context, MediaFileSystemBackend::MediaTaskRunner())));
}
DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal ||
url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal ||
url.type() == fileapi::kFileSystemTypeDrive);
......@@ -286,6 +301,9 @@ FileSystemBackend::CreateFileStreamReader(
return scoped_ptr<webkit_blob::FileStreamReader>(
webkit_blob::FileStreamReader::CreateForFileSystemFile(
context, url, offset, expected_modification_time));
case fileapi::kFileSystemTypeDeviceMediaAsFileStorage:
return mtp_delegate_->CreateFileStreamReader(
url, offset, expected_modification_time, context);
default:
NOTREACHED();
}
......@@ -316,6 +334,8 @@ FileSystemBackend::CreateFileStreamWriter(
case fileapi::kFileSystemTypeRestrictedNativeLocal:
// Restricted native local file system is read only.
return scoped_ptr<fileapi::FileStreamWriter>();
case fileapi::kFileSystemTypeDeviceMediaAsFileStorage:
return mtp_delegate_->CreateFileStreamWriter(url, offset, context);
default:
NOTREACHED();
}
......
......@@ -70,6 +70,7 @@ class FileSystemBackend : public fileapi::ExternalFileSystemBackend {
FileSystemBackend(
FileSystemBackendDelegate* drive_delegate,
FileSystemBackendDelegate* file_system_provider_delegate,
FileSystemBackendDelegate* mtp_delegate,
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,
scoped_refptr<fileapi::ExternalMountPoints> mount_points,
fileapi::ExternalMountPoints* system_mount_points);
......@@ -138,6 +139,9 @@ class FileSystemBackend : public fileapi::ExternalFileSystemBackend {
// The delegate instance for the provided file system related operations.
scoped_ptr<FileSystemBackendDelegate> file_system_provider_delegate_;
// The delegate instance for the MTP file system related operations.
scoped_ptr<FileSystemBackendDelegate> mtp_delegate_;
// Mount points specific to the owning context (i.e. per-profile mount
// points).
//
......
......@@ -43,6 +43,7 @@ TEST(ChromeOSFileSystemBackendTest, DefaultMountPoints) {
chromeos::FileSystemBackend backend(
NULL, // drive_delegate
NULL, // file_system_provider_delegate
NULL, // mtp_delegate
storage_policy,
mount_points.get(),
fileapi::ExternalMountPoints::GetSystemInstance());
......@@ -71,6 +72,7 @@ TEST(ChromeOSFileSystemBackendTest, GetRootDirectories) {
chromeos::FileSystemBackend backend(NULL, // drive_delegate
NULL, // file_system_provider_delegate
NULL, // mtp_delegate
storage_policy,
mount_points.get(),
system_mount_points.get());
......@@ -117,6 +119,7 @@ TEST(ChromeOSFileSystemBackendTest, AccessPermissions) {
fileapi::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(NULL, // drive_delegate
NULL, // file_system_provider_delegate
NULL, // mtp_delegate
storage_policy,
mount_points.get(),
system_mount_points.get());
......@@ -210,6 +213,7 @@ TEST(ChromeOSFileSystemBackendTest, GetVirtualPathConflictWithSystemPoints) {
fileapi::ExternalMountPoints::CreateRefCounted());
chromeos::FileSystemBackend backend(NULL, // drive_delegate
NULL, // file_system_provider_delegate
NULL, // mtp_delegate
storage_policy,
mount_points.get(),
system_mount_points.get());
......
// 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/fileapi/mtp_file_system_backend_delegate.h"
#include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h"
#include "webkit/browser/blob/file_stream_reader.h"
#include "webkit/browser/fileapi/file_stream_writer.h"
#include "webkit/browser/fileapi/file_system_url.h"
namespace chromeos {
MTPFileSystemBackendDelegate::MTPFileSystemBackendDelegate(
const base::FilePath& storage_partition_path)
: device_media_async_file_util_(
DeviceMediaAsyncFileUtil::Create(storage_partition_path)) {
}
MTPFileSystemBackendDelegate::~MTPFileSystemBackendDelegate() {
}
fileapi::AsyncFileUtil* MTPFileSystemBackendDelegate::GetAsyncFileUtil(
fileapi::FileSystemType type) {
DCHECK_EQ(fileapi::kFileSystemTypeDeviceMediaAsFileStorage, type);
return device_media_async_file_util_.get();
}
scoped_ptr<webkit_blob::FileStreamReader>
MTPFileSystemBackendDelegate::CreateFileStreamReader(
const fileapi::FileSystemURL& url,
int64 offset,
const base::Time& expected_modification_time,
fileapi::FileSystemContext* context) {
DCHECK_EQ(fileapi::kFileSystemTypeDeviceMediaAsFileStorage, url.type());
// TODO(kinaba): Returned MediaFileStreamReader verifies file header and
// stops reading if the stream does not represent a media file. We might
// want to remove the verification here since we want to mount MTP devices
// as normal file storage in Chrome OS file manager.
return device_media_async_file_util_->GetFileStreamReader(
url, offset, expected_modification_time, context);
}
scoped_ptr<fileapi::FileStreamWriter>
MTPFileSystemBackendDelegate::CreateFileStreamWriter(
const fileapi::FileSystemURL& url,
int64 offset,
fileapi::FileSystemContext* context) {
DCHECK_EQ(fileapi::kFileSystemTypeDeviceMediaAsFileStorage, url.type());
// TODO(kinaba): support writing.
return scoped_ptr<fileapi::FileStreamWriter>();
}
} // namespace chromeos
// 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_FILEAPI_MTP_FILE_SYSTEM_BACKEND_DELEGATE_H_
#define CHROME_BROWSER_CHROMEOS_FILEAPI_MTP_FILE_SYSTEM_BACKEND_DELEGATE_H_
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
namespace base {
class FilePath;
} // namespace base
class DeviceMediaAsyncFileUtil;
namespace chromeos {
// This is delegate interface to inject the MTP device file system in Chrome OS
// file API backend.
class MTPFileSystemBackendDelegate : public FileSystemBackendDelegate {
public:
explicit MTPFileSystemBackendDelegate(
const base::FilePath& storage_partition_path);
virtual ~MTPFileSystemBackendDelegate();
// FileSystemBackendDelegate overrides.
virtual fileapi::AsyncFileUtil* GetAsyncFileUtil(
fileapi::FileSystemType type) OVERRIDE;
virtual scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader(
const fileapi::FileSystemURL& url,
int64 offset,
const base::Time& expected_modification_time,
fileapi::FileSystemContext* context) OVERRIDE;
virtual scoped_ptr<fileapi::FileStreamWriter> CreateFileStreamWriter(
const fileapi::FileSystemURL& url,
int64 offset,
fileapi::FileSystemContext* context) OVERRIDE;
private:
scoped_ptr<DeviceMediaAsyncFileUtil> device_media_async_file_util_;
DISALLOW_COPY_AND_ASSIGN(MTPFileSystemBackendDelegate);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_FILEAPI_MTP_FILE_SYSTEM_BACKEND_DELEGATE_H_
......@@ -395,6 +395,8 @@
'browser/chromeos/fileapi/file_system_backend.cc',
'browser/chromeos/fileapi/file_system_backend.h',
'browser/chromeos/fileapi/file_system_backend_delegate.h',
'browser/chromeos/fileapi/mtp_file_system_backend_delegate.cc',
'browser/chromeos/fileapi/mtp_file_system_backend_delegate.h',
'browser/chromeos/first_run/drive_first_run_controller.cc',
'browser/chromeos/first_run/drive_first_run_controller.h',
'browser/chromeos/first_run/first_run.cc',
......
......@@ -180,6 +180,12 @@ bool StorageInfo::IsPicasaDevice(const std::string& device_id) {
return CrackDeviceId(device_id, &type, NULL) && type == PICASA;
}
// static
bool StorageInfo::IsMTPDevice(const std::string& device_id) {
Type type;
return CrackDeviceId(device_id, &type, NULL) && type == MTP_OR_PTP;
}
base::string16 StorageInfo::GetDisplayName(bool with_size) const {
return GetDisplayNameWithOverride(base::string16(), with_size);
}
......
......@@ -69,6 +69,8 @@ class StorageInfo {
static bool IsIPhotoDevice(const std::string& device_id);
static bool IsMTPDevice(const std::string& device_id);
// Get the display name for the removable device represented by this
// StorageInfo. Include the size for removable devices if |with_size| is true.
base::string16 GetDisplayName(bool with_size) const;
......
......@@ -31,7 +31,8 @@ base::FilePath NormalizeFilePath(const base::FilePath& path) {
}
bool IsOverlappingMountPathForbidden(fileapi::FileSystemType type) {
return type != fileapi::kFileSystemTypeNativeMedia;
return type != fileapi::kFileSystemTypeNativeMedia &&
type != fileapi::kFileSystemTypeDeviceMedia;
}
// Wrapper around ref-counted ExternalMountPoints that will be used to lazily
......
......@@ -91,6 +91,7 @@ int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
case kFileSystemTypeNativeLocal:
case kFileSystemTypeCloudDevice:
case kFileSystemTypeProvided:
case kFileSystemTypeDeviceMediaAsFileStorage:
return FILE_PERMISSION_USE_FILE_PERMISSION;
case kFileSystemTypeRestrictedNativeLocal:
......
......@@ -122,6 +122,10 @@ enum FileSystemType {
// A filesystem that is mounted via the FileSystemProvider API.
kFileSystemTypeProvided,
// A media filesystem such as MTP or PTP, mounted as a file storage not
// limited to media files.
kFileSystemTypeDeviceMediaAsFileStorage,
// --------------------------------------------------------------------
// Marks the end of internal type enum. (This is not the actual fs type)
// New internal filesystem types must be added above this line.
......
......@@ -319,6 +319,8 @@ std::string GetFileSystemTypeString(FileSystemType type) {
return "CloudDevice";
case kFileSystemTypeProvided:
return "Provided";
case kFileSystemTypeDeviceMediaAsFileStorage:
return "DeviceMediaStorage";
case kFileSystemInternalTypeEnumStart:
case kFileSystemInternalTypeEnumEnd:
NOTREACHED();
......
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