| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/chromeos/arc/arc_downloads_watcher_service.h" |
| 6 |
| 7 #include <memory> |
| 8 #include <utility> |
| 9 |
| 10 #include "base/callback.h" |
| 11 #include "base/files/file_enumerator.h" |
| 12 #include "base/files/file_path.h" |
| 13 #include "base/files/file_path_watcher.h" |
| 14 #include "base/memory/ptr_util.h" |
| 15 #include "base/time/time.h" |
| 16 #include "chrome/browser/download/download_prefs.h" |
| 17 #include "chrome/browser/profiles/profile_manager.h" |
| 18 #include "chrome/common/chrome_paths.h" |
| 19 #include "components/arc/arc_bridge_service.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "mojo/public/cpp/bindings/array.h" |
| 22 |
| 23 using content::BrowserThread; |
| 24 |
| 25 // Mapping from Android file paths to last modified timestamps. |
| 26 using TimestampMap = std::map<base::FilePath, base::Time>; |
| 27 |
| 28 static const base::FilePath::CharType kAndroidDownloadDir[] = |
| 29 FILE_PATH_LITERAL("/storage/emulated/0/Download"); |
| 30 |
| 31 namespace arc { |
| 32 |
| 33 namespace { |
| 34 |
| 35 // Compares two TimestampMaps and returns the list of file paths added/removed |
| 36 // or whose timestamp have changed. |
| 37 std::vector<base::FilePath> CollectChangedPaths( |
| 38 const TimestampMap& timestamp_map_a, |
| 39 const TimestampMap& timestamp_map_b) { |
| 40 std::vector<base::FilePath> changed_paths; |
| 41 |
| 42 TimestampMap::const_iterator iter_a = timestamp_map_a.begin(); |
| 43 TimestampMap::const_iterator iter_b = timestamp_map_b.begin(); |
| 44 while (iter_a != timestamp_map_a.end() && iter_b != timestamp_map_b.end()) { |
| 45 if (iter_a->first == iter_b->first) { |
| 46 if (iter_a->second != iter_b->second) { |
| 47 changed_paths.emplace_back(iter_a->first); |
| 48 } |
| 49 ++iter_a; |
| 50 ++iter_b; |
| 51 } else if (iter_a->first < iter_b->first) { |
| 52 changed_paths.emplace_back(iter_a->first); |
| 53 ++iter_a; |
| 54 } else { // iter_a->first > iter_b->first |
| 55 changed_paths.emplace_back(iter_b->first); |
| 56 ++iter_b; |
| 57 } |
| 58 } |
| 59 |
| 60 while (iter_a != timestamp_map_a.end()) { |
| 61 changed_paths.emplace_back(iter_a->first); |
| 62 ++iter_a; |
| 63 } |
| 64 while (iter_b != timestamp_map_b.end()) { |
| 65 changed_paths.emplace_back(iter_b->first); |
| 66 ++iter_b; |
| 67 } |
| 68 |
| 69 return changed_paths; |
| 70 } |
| 71 |
| 72 } // namespace |
| 73 |
| 74 // The core part of ArcDownloadsWatcherService to watch for file changes in |
| 75 // Downloads directory. |
| 76 class ArcDownloadsWatcherService::DownloadsWatcher { |
| 77 public: |
| 78 using Callback = |
| 79 base::Callback<void(const std::vector<base::FilePath>& paths)>; |
| 80 |
| 81 explicit DownloadsWatcher(const Callback& callback); |
| 82 ~DownloadsWatcher(); |
| 83 |
| 84 // Starts watching Downloads directory. |
| 85 void Start(); |
| 86 |
| 87 private: |
| 88 // Called by base::FilePathWatcher to notify file changes. |
| 89 void OnFilePathChanged(const base::FilePath& path, bool error); |
| 90 |
| 91 // Scans files under |downloads_dir_| recursively and builds a map from file |
| 92 // paths (in Android filesystem) to last modified timestamps. |
| 93 TimestampMap BuildTimestampMap() const; |
| 94 |
| 95 Callback callback_; |
| 96 base::FilePath downloads_dir_; |
| 97 std::unique_ptr<base::FilePathWatcher> watcher_; |
| 98 TimestampMap last_timestamp_map_; |
| 99 |
| 100 // Note: This should remain the last member so it'll be destroyed and |
| 101 // invalidate the weak pointers before any other members are destroyed. |
| 102 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_; |
| 103 |
| 104 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher); |
| 105 }; |
| 106 |
| 107 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher( |
| 108 const Callback& callback) |
| 109 : callback_(callback), weak_ptr_factory_(this) { |
| 110 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 111 |
| 112 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile()) |
| 113 .GetDefaultDownloadDirectoryForProfile() |
| 114 .StripTrailingSeparators(); |
| 115 } |
| 116 |
| 117 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() { |
| 118 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 119 } |
| 120 |
| 121 void ArcDownloadsWatcherService::DownloadsWatcher::Start() { |
| 122 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 123 |
| 124 // Initialize with the current timestamp map and avoid initial notification. |
| 125 // It is not needed since MediaProvider scans whole storage area on boot. |
| 126 last_timestamp_map_ = BuildTimestampMap(); |
| 127 |
| 128 watcher_ = base::MakeUnique<base::FilePathWatcher>(); |
| 129 // On Linux, base::FilePathWatcher::Watch() always returns true. |
| 130 watcher_->Watch(downloads_dir_, true, |
| 131 base::Bind(&DownloadsWatcher::OnFilePathChanged, |
| 132 weak_ptr_factory_.GetWeakPtr())); |
| 133 } |
| 134 |
| 135 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged( |
| 136 const base::FilePath& path, |
| 137 bool error) { |
| 138 // On Linux, |error| is always false. Also, |path| is always the same path |
| 139 // as one given to FilePathWatcher::Watch(). |
| 140 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 141 |
| 142 TimestampMap current_timestamp_map = BuildTimestampMap(); |
| 143 |
| 144 std::vector<base::FilePath> changed_paths = |
| 145 CollectChangedPaths(last_timestamp_map_, current_timestamp_map); |
| 146 |
| 147 last_timestamp_map_ = std::move(current_timestamp_map); |
| 148 |
| 149 callback_.Run(changed_paths); |
| 150 } |
| 151 |
| 152 TimestampMap ArcDownloadsWatcherService::DownloadsWatcher::BuildTimestampMap() |
| 153 const { |
| 154 DCHECK(!downloads_dir_.EndsWithSeparator()); |
| 155 TimestampMap timestamp_map; |
| 156 |
| 157 // Enumerate normal files only; directories and symlinks are skipped. |
| 158 base::FileEnumerator enumerator(downloads_dir_, true, |
| 159 base::FileEnumerator::FILES); |
| 160 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty(); |
| 161 cros_path = enumerator.Next()) { |
| 162 // Android file path can be obtained by replacing |downloads_dir_| prefix |
| 163 // with |kAndroidDownloadDir|. |
| 164 const base::FilePath& android_path = |
| 165 base::FilePath(kAndroidDownloadDir) |
| 166 .Append( |
| 167 cros_path.value().substr(downloads_dir_.value().length() + 1)); |
| 168 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo(); |
| 169 timestamp_map[android_path] = info.GetLastModifiedTime(); |
| 170 } |
| 171 return timestamp_map; |
| 172 } |
| 173 |
| 174 ArcDownloadsWatcherService::ArcDownloadsWatcherService( |
| 175 ArcBridgeService* bridge_service) |
| 176 : ArcService(bridge_service), weak_ptr_factory_(this) { |
| 177 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 178 arc_bridge_service()->AddObserver(this); |
| 179 } |
| 180 |
| 181 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() { |
| 182 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 183 arc_bridge_service()->RemoveObserver(this); |
| 184 StopWatchingDownloads(); |
| 185 DCHECK(!watcher_.get()); |
| 186 } |
| 187 |
| 188 void ArcDownloadsWatcherService::OnFileSystemInstanceReady() { |
| 189 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 190 StartWatchingDownloads(); |
| 191 } |
| 192 |
| 193 void ArcDownloadsWatcherService::OnFileSystemInstanceClosed() { |
| 194 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 195 StopWatchingDownloads(); |
| 196 } |
| 197 |
| 198 void ArcDownloadsWatcherService::StartWatchingDownloads() { |
| 199 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 200 StopWatchingDownloads(); |
| 201 DCHECK(!watcher_.get()); |
| 202 watcher_ = base::MakeUnique<DownloadsWatcher>( |
| 203 base::Bind(&ArcDownloadsWatcherService::OnDownloadsChanged, |
| 204 weak_ptr_factory_.GetWeakPtr())); |
| 205 BrowserThread::PostTask( |
| 206 BrowserThread::FILE, FROM_HERE, |
| 207 base::Bind(&DownloadsWatcher::Start, base::Unretained(watcher_.get()))); |
| 208 } |
| 209 |
| 210 void ArcDownloadsWatcherService::StopWatchingDownloads() { |
| 211 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 212 if (watcher_.get()) { |
| 213 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, |
| 214 watcher_.release()); |
| 215 } |
| 216 } |
| 217 |
| 218 void ArcDownloadsWatcherService::OnDownloadsChanged( |
| 219 const std::vector<base::FilePath>& paths) { |
| 220 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 221 |
| 222 auto instance = arc_bridge_service()->file_system_instance(); |
| 223 if (!instance) { |
| 224 return; |
| 225 } |
| 226 |
| 227 mojo::Array<mojo::String> mojo_paths(paths.size()); |
| 228 for (size_t i = 0; i < paths.size(); ++i) { |
| 229 mojo_paths[i] = paths[i].value(); |
| 230 } |
| 231 instance->RequestMediaScan(std::move(mojo_paths)); |
| 232 } |
| 233 |
| 234 } // namespace arc |
| OLD | NEW |