Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/arc/arc_downloads_watcher_service.h" | 5 #include "chrome/browser/chromeos/arc/arc_downloads_watcher_service.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 ++iter_a; | 63 ++iter_a; |
| 64 } | 64 } |
| 65 while (iter_b != timestamp_map_b.end()) { | 65 while (iter_b != timestamp_map_b.end()) { |
| 66 changed_paths.emplace_back(iter_b->first); | 66 changed_paths.emplace_back(iter_b->first); |
| 67 ++iter_b; | 67 ++iter_b; |
| 68 } | 68 } |
| 69 | 69 |
| 70 return changed_paths; | 70 return changed_paths; |
| 71 } | 71 } |
| 72 | 72 |
| 73 // Scans files under |downloads_dir| recursively and builds a map from file | |
| 74 // paths (in Android filesystem) to last modified timestamps. | |
| 75 TimestampMap BuildTimestampMap(base::FilePath downloads_dir) { | |
| 76 DCHECK(!downloads_dir.EndsWithSeparator()); | |
| 77 TimestampMap timestamp_map; | |
| 78 | |
| 79 // Enumerate normal files only; directories and symlinks are skipped. | |
| 80 base::FileEnumerator enumerator(downloads_dir, true, | |
| 81 base::FileEnumerator::FILES); | |
| 82 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty(); | |
| 83 cros_path = enumerator.Next()) { | |
| 84 // Android file path can be obtained by replacing |downloads_dir| prefix | |
| 85 // with |kAndroidDownloadDir|. | |
| 86 const base::FilePath& android_path = | |
| 87 base::FilePath(kAndroidDownloadDir) | |
| 88 .Append( | |
| 89 cros_path.value().substr(downloads_dir.value().length() + 1)); | |
| 90 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo(); | |
| 91 timestamp_map[android_path] = info.GetLastModifiedTime(); | |
| 92 } | |
| 93 return timestamp_map; | |
| 94 } | |
| 95 | |
| 96 std::pair<base::Time, TimestampMap> BuildTimestampMapCallback( | |
| 97 base::FilePath downloads_dir) { | |
| 98 base::Time update_time = base::Time::Now(); | |
| 99 TimestampMap current_timestamp_map = BuildTimestampMap(downloads_dir); | |
| 100 return std::pair<base::Time, TimestampMap>(update_time, | |
|
Shuhei Takahashi
2016/09/08 03:43:48
FYI, std::make_pair is useful to avoid explicit te
dspaid
2016/09/08 03:59:56
Done.
| |
| 101 current_timestamp_map); | |
|
Shuhei Takahashi
2016/09/08 03:43:48
IIUC you need std::move() here to avoid copy.
dspaid
2016/09/08 03:59:56
Done.
| |
| 102 } | |
| 103 | |
| 73 } // namespace | 104 } // namespace |
| 74 | 105 |
| 75 // The core part of ArcDownloadsWatcherService to watch for file changes in | 106 // The core part of ArcDownloadsWatcherService to watch for file changes in |
| 76 // Downloads directory. | 107 // Downloads directory. |
| 77 class ArcDownloadsWatcherService::DownloadsWatcher { | 108 class ArcDownloadsWatcherService::DownloadsWatcher { |
| 78 public: | 109 public: |
| 79 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>; | 110 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>; |
| 80 | 111 |
| 81 explicit DownloadsWatcher(const Callback& callback); | 112 explicit DownloadsWatcher(const Callback& callback); |
| 82 ~DownloadsWatcher(); | 113 ~DownloadsWatcher(); |
| 83 | 114 |
| 84 // Starts watching Downloads directory. | 115 // Starts watching Downloads directory. |
| 85 void Start(); | 116 void Start(); |
| 86 | 117 |
| 87 private: | 118 private: |
| 88 // Called by base::FilePathWatcher to notify file changes. | 119 // Called by base::FilePathWatcher to notify file changes. |
| 120 // Wrapper around HandleFilePathChanged to pass the notification timestamp. | |
|
Shuhei Takahashi
2016/09/08 03:43:48
This line is outdated.
dspaid
2016/09/08 03:59:56
Done.
| |
| 89 void OnFilePathChanged(const base::FilePath& path, bool error); | 121 void OnFilePathChanged(const base::FilePath& path, bool error); |
| 90 | 122 |
| 91 // Scans files under |downloads_dir_| recursively and builds a map from file | 123 // If there has not been a full scan of updated files since notify_time, |
| 92 // paths (in Android filesystem) to last modified timestamps. | 124 // update the last_timestamp_map_ and send any new/changed files to the |
| 93 TimestampMap BuildTimestampMap() const; | 125 // android media scanner. |
| 126 void HandleFilePathChanged(base::Time notify_time); | |
|
Shuhei Takahashi
2016/09/08 03:43:48
This function is no longer defined.
dspaid
2016/09/08 03:59:56
Done.
| |
| 127 | |
| 128 void OnBuildTimestampMap( | |
| 129 std::pair<base::Time, TimestampMap> timestamp_and_map); | |
| 94 | 130 |
| 95 Callback callback_; | 131 Callback callback_; |
| 96 base::FilePath downloads_dir_; | 132 base::FilePath downloads_dir_; |
| 97 std::unique_ptr<base::FilePathWatcher> watcher_; | 133 std::unique_ptr<base::FilePathWatcher> watcher_; |
| 98 TimestampMap last_timestamp_map_; | 134 TimestampMap last_timestamp_map_; |
| 135 // The timestamp of the last OnFilePathChanged callback received. | |
| 136 base::Time last_update_time_; | |
|
Shuhei Takahashi
2016/09/08 03:43:48
How about using "bool pending_task_" instead? Thou
dspaid
2016/09/08 03:59:56
The small timing difference actually has a reasona
Shuhei Takahashi
2016/09/08 04:25:48
I see. But then I think it's a bit optimistic to a
dspaid
2016/09/09 02:10:45
Experimentally I'm seeing a reasonable success rat
Shuhei Takahashi
2016/09/09 03:18:51
It is best if we have PostDelayedTaskAndReplyWithR
| |
| 137 // Whether or not there is an outstanding task to update last_timestamp_map_. | |
| 138 bool outstanding_task_; | |
| 99 | 139 |
| 100 // Note: This should remain the last member so it'll be destroyed and | 140 // Note: This should remain the last member so it'll be destroyed and |
| 101 // invalidate the weak pointers before any other members are destroyed. | 141 // invalidate the weak pointers before any other members are destroyed. |
| 102 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_; | 142 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_; |
| 103 | 143 |
| 104 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher); | 144 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher); |
| 105 }; | 145 }; |
| 106 | 146 |
| 107 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher( | 147 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher( |
| 108 const Callback& callback) | 148 const Callback& callback) |
| 109 : callback_(callback), weak_ptr_factory_(this) { | 149 : callback_(callback), |
| 150 last_update_time_(base::Time()), | |
| 151 outstanding_task_(false), | |
| 152 weak_ptr_factory_(this) { | |
| 110 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 153 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 111 | 154 |
| 112 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile()) | 155 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile()) |
| 113 .GetDefaultDownloadDirectoryForProfile() | 156 .GetDefaultDownloadDirectoryForProfile() |
| 114 .StripTrailingSeparators(); | 157 .StripTrailingSeparators(); |
| 115 } | 158 } |
| 116 | 159 |
| 117 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() { | 160 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() { |
| 118 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 161 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 119 } | 162 } |
| 120 | 163 |
| 121 void ArcDownloadsWatcherService::DownloadsWatcher::Start() { | 164 void ArcDownloadsWatcherService::DownloadsWatcher::Start() { |
| 122 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 165 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 123 | 166 |
| 124 // Initialize with the current timestamp map and avoid initial notification. | 167 // Initialize with the current timestamp map and avoid initial notification. |
| 125 // It is not needed since MediaProvider scans whole storage area on boot. | 168 // It is not needed since MediaProvider scans whole storage area on boot. |
| 126 last_timestamp_map_ = BuildTimestampMap(); | 169 last_update_time_ = base::Time::Now(); |
| 170 last_timestamp_map_ = BuildTimestampMap(downloads_dir_); | |
| 171 | |
| 172 outstanding_task_ = false; | |
| 127 | 173 |
| 128 watcher_ = base::MakeUnique<base::FilePathWatcher>(); | 174 watcher_ = base::MakeUnique<base::FilePathWatcher>(); |
| 129 // On Linux, base::FilePathWatcher::Watch() always returns true. | 175 // On Linux, base::FilePathWatcher::Watch() always returns true. |
| 130 watcher_->Watch(downloads_dir_, true, | 176 watcher_->Watch(downloads_dir_, true, |
| 131 base::Bind(&DownloadsWatcher::OnFilePathChanged, | 177 base::Bind(&DownloadsWatcher::OnFilePathChanged, |
| 132 weak_ptr_factory_.GetWeakPtr())); | 178 weak_ptr_factory_.GetWeakPtr())); |
| 133 } | 179 } |
| 134 | 180 |
| 135 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged( | 181 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged( |
| 136 const base::FilePath& path, | 182 const base::FilePath& path, |
| 137 bool error) { | 183 bool error) { |
| 138 // On Linux, |error| is always false. Also, |path| is always the same path | 184 // On Linux, |error| is always false. Also, |path| is always the same path |
| 139 // as one given to FilePathWatcher::Watch(). | 185 // as one given to FilePathWatcher::Watch(). |
| 140 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 186 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 187 if (!outstanding_task_) { | |
| 188 base::PostTaskAndReplyWithResult( | |
| 189 BrowserThread::GetBlockingPool(), FROM_HERE, | |
| 190 base::Bind(&BuildTimestampMapCallback, downloads_dir_), | |
| 191 base::Bind(&DownloadsWatcher::OnBuildTimestampMap, | |
| 192 weak_ptr_factory_.GetWeakPtr())); | |
| 193 outstanding_task_ = true; | |
| 194 } else { | |
| 195 last_update_time_ = base::Time::Now(); | |
| 196 } | |
| 197 } | |
| 141 | 198 |
| 142 TimestampMap current_timestamp_map = BuildTimestampMap(); | 199 void ArcDownloadsWatcherService::DownloadsWatcher::OnBuildTimestampMap( |
| 143 | 200 std::pair<base::Time, TimestampMap> timestamp_and_map) { |
| 201 TimestampMap current_timestamp_map = timestamp_and_map.second; | |
|
Shuhei Takahashi
2016/09/08 03:43:48
Could you insert DCHECK_CURRENTLY_ON?
Shuhei Takahashi
2016/09/08 03:43:48
Also, could you insert DCHECK(outstanding_task_)?
dspaid
2016/09/08 03:59:56
Done.
dspaid
2016/09/08 03:59:57
Done.
| |
| 144 std::vector<base::FilePath> changed_paths = | 202 std::vector<base::FilePath> changed_paths = |
| 145 CollectChangedPaths(last_timestamp_map_, current_timestamp_map); | 203 CollectChangedPaths(last_timestamp_map_, current_timestamp_map); |
| 146 | 204 |
| 147 last_timestamp_map_ = std::move(current_timestamp_map); | 205 last_timestamp_map_ = std::move(current_timestamp_map); |
| 148 | 206 |
| 149 mojo::Array<mojo::String> mojo_paths(changed_paths.size()); | 207 mojo::Array<mojo::String> mojo_paths(changed_paths.size()); |
| 150 for (size_t i = 0; i < changed_paths.size(); ++i) { | 208 for (size_t i = 0; i < changed_paths.size(); ++i) { |
| 151 mojo_paths[i] = changed_paths[i].value(); | 209 mojo_paths[i] = changed_paths[i].value(); |
| 152 } | 210 } |
| 153 BrowserThread::PostTask( | 211 BrowserThread::PostTask( |
| 154 BrowserThread::UI, FROM_HERE, | 212 BrowserThread::UI, FROM_HERE, |
| 155 base::Bind(callback_, base::Passed(std::move(mojo_paths)))); | 213 base::Bind(callback_, base::Passed(std::move(mojo_paths)))); |
| 156 } | 214 if (last_update_time_ > timestamp_and_map.first) { |
| 157 | 215 base::PostTaskAndReplyWithResult( |
| 158 TimestampMap ArcDownloadsWatcherService::DownloadsWatcher::BuildTimestampMap() | 216 BrowserThread::GetBlockingPool(), FROM_HERE, |
| 159 const { | 217 base::Bind(&BuildTimestampMapCallback, downloads_dir_), |
| 160 DCHECK(!downloads_dir_.EndsWithSeparator()); | 218 base::Bind(&DownloadsWatcher::OnBuildTimestampMap, |
| 161 TimestampMap timestamp_map; | 219 weak_ptr_factory_.GetWeakPtr())); |
| 162 | |
| 163 // Enumerate normal files only; directories and symlinks are skipped. | |
| 164 base::FileEnumerator enumerator(downloads_dir_, true, | |
| 165 base::FileEnumerator::FILES); | |
| 166 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty(); | |
| 167 cros_path = enumerator.Next()) { | |
| 168 // Android file path can be obtained by replacing |downloads_dir_| prefix | |
| 169 // with |kAndroidDownloadDir|. | |
| 170 const base::FilePath& android_path = | |
| 171 base::FilePath(kAndroidDownloadDir) | |
| 172 .Append( | |
| 173 cros_path.value().substr(downloads_dir_.value().length() + 1)); | |
| 174 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo(); | |
| 175 timestamp_map[android_path] = info.GetLastModifiedTime(); | |
| 176 } | 220 } |
| 177 return timestamp_map; | 221 outstanding_task_ = false; |
|
Shuhei Takahashi
2016/09/08 03:43:48
We must keep |outstanding_task_| true when there w
dspaid
2016/09/08 03:59:56
Done.
| |
| 178 } | 222 } |
| 179 | 223 |
| 180 ArcDownloadsWatcherService::ArcDownloadsWatcherService( | 224 ArcDownloadsWatcherService::ArcDownloadsWatcherService( |
| 181 ArcBridgeService* bridge_service) | 225 ArcBridgeService* bridge_service) |
| 182 : ArcService(bridge_service), weak_ptr_factory_(this) { | 226 : ArcService(bridge_service), weak_ptr_factory_(this) { |
| 183 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 227 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 184 arc_bridge_service()->file_system()->AddObserver(this); | 228 arc_bridge_service()->file_system()->AddObserver(this); |
| 185 } | 229 } |
| 186 | 230 |
| 187 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() { | 231 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 225 mojo::Array<mojo::String> paths) { | 269 mojo::Array<mojo::String> paths) { |
| 226 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 270 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 227 | 271 |
| 228 auto* instance = arc_bridge_service()->file_system()->instance(); | 272 auto* instance = arc_bridge_service()->file_system()->instance(); |
| 229 if (!instance) | 273 if (!instance) |
| 230 return; | 274 return; |
| 231 instance->RequestMediaScan(std::move(paths)); | 275 instance->RequestMediaScan(std::move(paths)); |
| 232 } | 276 } |
| 233 | 277 |
| 234 } // namespace arc | 278 } // namespace arc |
| OLD | NEW |