Skip to content

Commit

Permalink
mediaview: Mount ARC documents provider file system volumes.
Browse files Browse the repository at this point in the history
New media view volumes are added to chromeos::VolumeManager so that they are
available to Files.app. UI patches are coming later, so at this point it looks like
unusable unknown volumes appear in Files.app. To hide them from users for now,
the feature is guarded with base::FeatureList. Once UI is ready I will make them
enabled by default.

BUG=chromium:671511
TEST=New (unusable) volumes are shown with --enable-features=ArcMediaView
TEST=Media views work with local pending UI patches

Review-Url: https://codereview.chromium.org/2580303002
Cr-Commit-Position: refs/heads/master@{#443205}
  • Loading branch information
nya3jp authored and Commit bot committed Jan 12, 2017
1 parent 9b7705c commit 4b8fa62
Show file tree
Hide file tree
Showing 16 changed files with 978 additions and 269 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/chromeos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ source_set("chromeos") {
"arc/fileapi/arc_file_system_instance_util.h",
"arc/fileapi/arc_file_system_service.cc",
"arc/fileapi/arc_file_system_service.h",
"arc/fileapi/arc_media_view_util.cc",
"arc/fileapi/arc_media_view_util.h",
"arc/intent_helper/arc_external_protocol_dialog.cc",
"arc/intent_helper/arc_external_protocol_dialog.h",
"arc/intent_helper/arc_navigation_throttle.cc",
Expand Down
46 changes: 41 additions & 5 deletions chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,48 @@

namespace arc {

// This is based on net/base/escape.cc: net::(anonymous namespace)::Escape.
// TODO(nya): Consider consolidating this function with EscapeFileSystemId() in
// chrome/browser/chromeos/file_system_provider/mount_path_util.cc.
// This version differs from the other one in the point that dots are not always
// escaped because authorities often contain harmless dots.
std::string EscapePathComponent(const std::string& name) {
std::string escaped;
// Escape dots only when they forms a special file name.
if (name == "." || name == "..") {
base::ReplaceChars(name, ".", "%2E", &escaped);
return escaped;
}
// Escape % and / only.
for (size_t i = 0; i < name.size(); ++i) {
const char c = name[i];
if (c == '%' || c == '/')
base::StringAppendF(&escaped, "%%%02X", c);
else
escaped.push_back(c);
}
return escaped;
}

std::string UnescapePathComponent(const std::string& escaped) {
return net::UnescapeURLComponent(
escaped, net::UnescapeRule::PATH_SEPARATORS |
net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
}

const char kDocumentsProviderMountPointName[] = "arc-documents-provider";
const base::FilePath::CharType kDocumentsProviderMountPointPath[] =
"/special/arc-documents-provider";
const char kAndroidDirectoryMimeType[] = "vnd.android.document/directory";

base::FilePath GetDocumentsProviderMountPath(
const std::string& authority,
const std::string& root_document_id) {
return base::FilePath(kDocumentsProviderMountPointPath)
.Append(EscapePathComponent(authority))
.Append(EscapePathComponent(root_document_id));
}

bool ParseDocumentsProviderUrl(const storage::FileSystemURL& url,
std::string* authority,
std::string* root_document_id,
Expand All @@ -39,12 +76,11 @@ bool ParseDocumentsProviderUrl(const storage::FileSystemURL& url,
if (components.size() < 5)
return false;

*authority = components[3];
*root_document_id = components[4];
*authority = UnescapePathComponent(components[3]);
*root_document_id = UnescapePathComponent(components[4]);

base::FilePath root_path = base::FilePath(kDocumentsProviderMountPointPath)
.Append(*authority)
.Append(*root_document_id);
base::FilePath root_path =
GetDocumentsProviderMountPath(*authority, *root_document_id);
// Special case: AppendRelativePath() fails for identical paths.
if (url_path_stripped == root_path) {
path->clear();
Expand Down
21 changes: 21 additions & 0 deletions chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,28 @@ extern const base::FilePath::CharType kDocumentsProviderMountPointPath[];
// Defined as DocumentsContract.Document.MIME_TYPE_DIR in Android.
extern const char kAndroidDirectoryMimeType[];

// Escapes a string so it can be used as a file/directory name.
// [%/.] are escaped with percent-encoding.
// NOTE: This function is visible only for unit testing. Usually you should not
// call this function directly.
std::string EscapePathComponent(const std::string& name);

// Unescapes a string escaped by EscapePathComponent().
// NOTE: This function is visible only for unit testing. Usually you should not
// call this function directly.
std::string UnescapePathComponent(const std::string& escaped);

// Returns the path of a directory where the specified DocumentsProvider is
// mounted.
// Appropriate escaping is done to embed |authority| and |root_document_id| in
// a file path.
base::FilePath GetDocumentsProviderMountPath(
const std::string& authority,
const std::string& root_document_id);

// Parses a FileSystemURL pointing to ARC documents provider file system.
// Appropriate unescaping is done to extract |authority| and |root_document_id|
// from |url|.
// On success, true is returned. All arguments must not be nullptr.
bool ParseDocumentsProviderUrl(const storage::FileSystemURL& url,
std::string* authority,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,45 @@ namespace arc {

namespace {

TEST(ArcDocumentsProviderUtilTest, EscapePathComponent) {
EXPECT_EQ("", EscapePathComponent(""));
EXPECT_EQ("%2E", EscapePathComponent("."));
EXPECT_EQ("%2E%2E", EscapePathComponent(".."));
EXPECT_EQ("...", EscapePathComponent("..."));
EXPECT_EQ("example.com", EscapePathComponent("example.com"));
EXPECT_EQ("%2F%2F%2F", EscapePathComponent("///"));
EXPECT_EQ("100%25", EscapePathComponent("100%"));
EXPECT_EQ("a b", EscapePathComponent("a b"));
EXPECT_EQ("ねこ", EscapePathComponent("ねこ"));
}

TEST(ArcDocumentsProviderUtilTest, UnescapePathComponent) {
EXPECT_EQ("", UnescapePathComponent(""));
EXPECT_EQ(".", UnescapePathComponent("%2E"));
EXPECT_EQ("..", UnescapePathComponent("%2E%2E"));
EXPECT_EQ("...", UnescapePathComponent("..."));
EXPECT_EQ("example.com", UnescapePathComponent("example.com"));
EXPECT_EQ("///", UnescapePathComponent("%2F%2F%2F"));
EXPECT_EQ("100%", UnescapePathComponent("100%25"));
EXPECT_EQ("a b", UnescapePathComponent("a b"));
EXPECT_EQ("ねこ", UnescapePathComponent("ねこ"));
}

TEST(ArcDocumentsProviderUtilTest, GetDocumentsProviderMountPath) {
EXPECT_EQ("/special/arc-documents-provider/authority/document_id",
GetDocumentsProviderMountPath("authority", "document_id").value());
EXPECT_EQ("/special/arc-documents-provider/a b/a b",
GetDocumentsProviderMountPath("a b", "a b").value());
EXPECT_EQ("/special/arc-documents-provider/a%2Fb/a%2Fb",
GetDocumentsProviderMountPath("a/b", "a/b").value());
EXPECT_EQ("/special/arc-documents-provider/%2E/%2E",
GetDocumentsProviderMountPath(".", ".").value());
EXPECT_EQ("/special/arc-documents-provider/%2E%2E/%2E%2E",
GetDocumentsProviderMountPath("..", "..").value());
EXPECT_EQ("/special/arc-documents-provider/.../...",
GetDocumentsProviderMountPath("...", "...").value());
}

TEST(ArcDocumentsProviderUtilTest, ParseDocumentsProviderUrl) {
std::string authority;
std::string root_document_id;
Expand Down Expand Up @@ -118,6 +157,22 @@ TEST(ArcDocumentsProviderUtilTest, ParseDocumentsProviderUrlInvalidPath) {
&authority, &root_document_id, &path));
}

TEST(ArcDocumentsProviderUtilTest, ParseDocumentsProviderUrlUnescape) {
std::string authority;
std::string root_document_id;
base::FilePath path;

EXPECT_TRUE(ParseDocumentsProviderUrl(
storage::FileSystemURL::CreateForTest(
GURL(), storage::kFileSystemTypeArcDocumentsProvider,
base::FilePath(
"/special/arc-documents-provider/cats/ro%2Fot/home/calico.jpg")),
&authority, &root_document_id, &path));
EXPECT_EQ("cats", authority);
EXPECT_EQ("ro/ot", root_document_id);
EXPECT_EQ(FILE_PATH_LITERAL("home/calico.jpg"), path.value());
}

TEST(ArcDocumentsProviderUtilTest, ParseDocumentsProviderUrlUtf8) {
std::string authority;
std::string root_document_id;
Expand All @@ -135,8 +190,13 @@ TEST(ArcDocumentsProviderUtilTest, ParseDocumentsProviderUrlUtf8) {
}

TEST(ArcDocumentsProviderUtilTest, BuildDocumentUrl) {
EXPECT_EQ("content://Cat%20Provider/document/C%2B%2B",
BuildDocumentUrl("Cat Provider", "C++").spec());
EXPECT_EQ("content://authority/document/document_id",
BuildDocumentUrl("authority", "document_id").spec());
EXPECT_EQ("content://a%20b/document/a%20b",
BuildDocumentUrl("a b", "a b").spec());
EXPECT_EQ("content://a%2Fb/document/a%2Fb",
BuildDocumentUrl("a/b", "a/b").spec());
EXPECT_EQ("content://../document/..", BuildDocumentUrl("..", "..").spec());
}

} // namespace
Expand Down
27 changes: 27 additions & 0 deletions chrome/browser/chromeos/arc/fileapi/arc_file_system_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
#include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util.h"
#include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/file_system/arc_file_system_observer.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/fileapi/external_mount_points.h"

using content::BrowserThread;

namespace arc {

// static
const char ArcFileSystemService::kArcServiceName[] =
"arc::ArcFileSystemService";

ArcFileSystemService::ArcFileSystemService(ArcBridgeService* bridge_service)
: ArcService(bridge_service) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Expand All @@ -31,16 +36,38 @@ ArcFileSystemService::ArcFileSystemService(ArcBridgeService* bridge_service)
storage::kFileSystemTypeArcDocumentsProvider,
storage::FileSystemMountOption(),
base::FilePath(kDocumentsProviderMountPointPath));

arc_bridge_service()->file_system()->AddObserver(this);
}

ArcFileSystemService::~ArcFileSystemService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);

arc_bridge_service()->file_system()->RemoveObserver(this);

storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();

mount_points->RevokeFileSystem(kContentFileSystemMountPointName);
mount_points->RevokeFileSystem(kDocumentsProviderMountPointPath);
}

void ArcFileSystemService::AddObserver(ArcFileSystemObserver* observer) {
observer_list_.AddObserver(observer);
}

void ArcFileSystemService::RemoveObserver(ArcFileSystemObserver* observer) {
observer_list_.RemoveObserver(observer);
}

void ArcFileSystemService::OnInstanceReady() {
for (auto& observer : observer_list_)
observer.OnFileSystemsReady();
}

void ArcFileSystemService::OnInstanceClosed() {
for (auto& observer : observer_list_)
observer.OnFileSystemsClosed();
}

} // namespace arc
20 changes: 19 additions & 1 deletion chrome/browser/chromeos/arc/fileapi/arc_file_system_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,37 @@
#define CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_FILE_SYSTEM_SERVICE_H_

#include "base/macros.h"
#include "base/observer_list.h"
#include "components/arc/arc_service.h"
#include "components/arc/common/file_system.mojom.h"
#include "components/arc/instance_holder.h"

namespace arc {

class ArcBridgeService;
class ArcFileSystemObserver;

// ArcFileSystemService registers ARC file systems to the system.
class ArcFileSystemService : public ArcService {
class ArcFileSystemService
: public ArcService,
public InstanceHolder<mojom::FileSystemInstance>::Observer {
public:
explicit ArcFileSystemService(ArcBridgeService* bridge_service);
~ArcFileSystemService() override;

void AddObserver(ArcFileSystemObserver* observer);
void RemoveObserver(ArcFileSystemObserver* observer);

// InstanceHolder<mojom::FileSystemInstance>::Observer overrides:
void OnInstanceReady() override;
void OnInstanceClosed() override;

// For supporting ArcServiceManager::GetService<T>().
static const char kArcServiceName[];

private:
base::ObserverList<ArcFileSystemObserver> observer_list_;

DISALLOW_COPY_AND_ASSIGN(ArcFileSystemService);
};

Expand Down
29 changes: 29 additions & 0 deletions chrome/browser/chromeos/arc/fileapi/arc_media_view_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2017 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/arc/fileapi/arc_media_view_util.h"

namespace arc {

namespace {

constexpr char kMediaViewVolumeIdPrefix[] = "media_view:";

} // namespace

const base::Feature kMediaViewFeature{"ArcMediaView",
base::FEATURE_DISABLED_BY_DEFAULT};

const char kMediaDocumentsProviderAuthority[] =
"com.android.providers.media.documents";

const char kImagesRootDocumentId[] = "images_root";
const char kVideosRootDocumentId[] = "videos_root";
const char kAudioRootDocumentId[] = "audio_root";

std::string GetMediaViewVolumeId(const std::string& root_document_id) {
return std::string(kMediaViewVolumeIdPrefix) + root_document_id;
}

} // namespace arc
32 changes: 32 additions & 0 deletions chrome/browser/chromeos/arc/fileapi/arc_media_view_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2017 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.
//
// Utilities for ARC Media View.

#ifndef CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_MEDIA_VIEW_UTIL_H_
#define CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_MEDIA_VIEW_UTIL_H_

#include <string>

#include "base/feature_list.h"

namespace arc {

// base::FeatureList feature for ARC media view.
extern const base::Feature kMediaViewFeature;

// Authority of MediaDocumentsProvider in Android.
extern const char kMediaDocumentsProviderAuthority[];

// Document IDs of file system roots in MediaDocumentsProvider.
extern const char kImagesRootDocumentId[];
extern const char kVideosRootDocumentId[];
extern const char kAudioRootDocumentId[];

// Returns an ID of a Media View volume.
std::string GetMediaViewVolumeId(const std::string& root_document_id);

} // namespace arc

#endif // CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_MEDIA_VIEW_UTIL_H_
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ void VolumeToVolumeMetadata(
case VOLUME_TYPE_MTP:
volume_metadata->volume_type = file_manager_private::VOLUME_TYPE_MTP;
break;
case VOLUME_TYPE_MEDIA_VIEW:
volume_metadata->volume_type =
file_manager_private::VOLUME_TYPE_MEDIA_VIEW;
break;
case VOLUME_TYPE_TESTING:
volume_metadata->volume_type =
file_manager_private::VOLUME_TYPE_TESTING;
Expand Down
Loading

0 comments on commit 4b8fa62

Please sign in to comment.