OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // MediaDeviceNotificationsLinux implementation. |
| 6 |
5 #include "content/browser/media_device_notifications_linux.h" | 7 #include "content/browser/media_device_notifications_linux.h" |
6 | 8 |
7 #include <mntent.h> | 9 #include <mntent.h> |
8 #include <stdio.h> | 10 #include <stdio.h> |
9 | 11 |
| 12 #include <vector> |
| 13 |
10 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/file_path.h" |
11 #include "base/file_util.h" | 16 #include "base/file_util.h" |
12 #include "base/string_util.h" | 17 #include "base/string_util.h" |
13 #include "base/system_monitor/system_monitor.h" | 18 #include "base/system_monitor/system_monitor.h" |
14 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
15 | 20 |
16 using base::SystemMonitor; | |
17 | |
18 namespace { | 21 namespace { |
19 | 22 |
20 const char* const kDCIMDirName = "DCIM"; | 23 const char kDCIMDirName[] = "DCIM"; |
21 | 24 |
22 // List of file systems we care about. | 25 // List of file systems we care about. |
23 const char* const kKnownFileSystems[] = { | 26 const char* const kKnownFileSystems[] = { |
24 "ext2", | 27 "ext2", |
25 "ext3", | 28 "ext3", |
26 "ext4", | 29 "ext4", |
27 "fat", | 30 "fat", |
28 "hfsplus", | 31 "hfsplus", |
29 "iso9660", | 32 "iso9660", |
30 "msdos", | 33 "msdos", |
31 "ntfs", | 34 "ntfs", |
32 "udf", | 35 "udf", |
33 "vfat", | 36 "vfat", |
34 }; | 37 }; |
35 | 38 |
36 } // namespace | 39 } // namespace |
37 | 40 |
38 namespace content { | 41 namespace content { |
39 | 42 |
| 43 using base::SystemMonitor; |
| 44 |
| 45 // A simple pass-through class. MediaDeviceNotificationsLinux cannot directly |
| 46 // inherit from FilePathWatcher::Delegate due to multiple inheritance. |
| 47 class MediaDeviceNotificationsLinux::WatcherDelegate |
| 48 : public base::files::FilePathWatcher::Delegate { |
| 49 public: |
| 50 explicit WatcherDelegate(MediaDeviceNotificationsLinux* notifier); |
| 51 |
| 52 // base::files::FilePathWatcher::Delegate implementation. |
| 53 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE; |
| 54 |
| 55 private: |
| 56 friend class base::RefCountedThreadSafe<WatcherDelegate>; |
| 57 |
| 58 // Avoids code deleting the object while there are references to it. |
| 59 // Aside from the base::RefCountedThreadSafe friend class, any attempts to |
| 60 // call this dtor will result in a compile-time error. |
| 61 virtual ~WatcherDelegate(); |
| 62 |
| 63 // The MediaDeviceNotificationsLinux instance that owns this WatcherDelegate. |
| 64 // Since |notifier_| will destroy this WatcherDelegate before it goes away, |
| 65 // the pointer is always valid. No need to add a reference count, as that |
| 66 // would create a circular reference. |
| 67 MediaDeviceNotificationsLinux* const notifier_; |
| 68 |
| 69 DISALLOW_COPY_AND_ASSIGN(WatcherDelegate); |
| 70 }; |
| 71 |
| 72 MediaDeviceNotificationsLinux::WatcherDelegate::WatcherDelegate( |
| 73 MediaDeviceNotificationsLinux* notifier) |
| 74 : notifier_(notifier) { |
| 75 } |
| 76 |
| 77 MediaDeviceNotificationsLinux::WatcherDelegate::~WatcherDelegate() { |
| 78 } |
| 79 |
| 80 void MediaDeviceNotificationsLinux::WatcherDelegate::OnFilePathChanged( |
| 81 const FilePath& path) { |
| 82 notifier_->OnFilePathChanged(path); |
| 83 } |
| 84 |
40 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( | 85 MediaDeviceNotificationsLinux::MediaDeviceNotificationsLinux( |
41 const FilePath& path) | 86 const FilePath& path) |
42 : initialized_(false), | 87 : initialized_(false), |
43 mtab_path_(path), | 88 mtab_path_(path), |
44 current_device_id_(0U) { | 89 current_device_id_(0U) { |
45 CHECK(!path.empty()); | 90 CHECK(!path.empty()); |
46 | 91 |
47 // Put |kKnownFileSystems| in std::set to get O(log N) access time. | 92 // Put |kKnownFileSystems| in std::set to get O(log N) access time. |
48 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) | 93 for (size_t i = 0; i < arraysize(kKnownFileSystems); ++i) { |
49 known_file_systems_.insert(kKnownFileSystems[i]); | 94 known_file_systems_.insert(kKnownFileSystems[i]); |
| 95 } |
50 } | 96 } |
51 | 97 |
52 MediaDeviceNotificationsLinux::~MediaDeviceNotificationsLinux() { | 98 MediaDeviceNotificationsLinux::~MediaDeviceNotificationsLinux() { |
53 } | 99 } |
54 | 100 |
55 void MediaDeviceNotificationsLinux::Init() { | 101 void MediaDeviceNotificationsLinux::Init() { |
56 BrowserThread::PostTask( | 102 BrowserThread::PostTask( |
57 BrowserThread::FILE, FROM_HERE, | 103 BrowserThread::FILE, FROM_HERE, |
58 base::Bind(&MediaDeviceNotificationsLinux::InitOnFileThread, this)); | 104 base::Bind(&MediaDeviceNotificationsLinux::InitOnFileThread, this)); |
59 } | 105 } |
60 | 106 |
61 void MediaDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path) { | 107 void MediaDeviceNotificationsLinux::OnFilePathChanged(const FilePath& path) { |
62 if (path != mtab_path_) { | 108 if (path != mtab_path_) { |
| 109 // This cannot happen unless FileWatcher is buggy. Just ignore this |
| 110 // notification and do nothing. |
63 NOTREACHED(); | 111 NOTREACHED(); |
64 return; | 112 return; |
65 } | 113 } |
66 | 114 |
67 UpdateMtab(); | 115 UpdateMtab(); |
68 } | 116 } |
69 | 117 |
70 void MediaDeviceNotificationsLinux::InitOnFileThread() { | 118 void MediaDeviceNotificationsLinux::InitOnFileThread() { |
71 DCHECK(!initialized_); | 119 DCHECK(!initialized_); |
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
73 initialized_ = true; | 121 initialized_ = true; |
74 | 122 |
75 watcher_delegate_ = new WatcherDelegate(this); | 123 watcher_delegate_ = new WatcherDelegate(this); |
76 if (!file_watcher_.Watch(mtab_path_, watcher_delegate_)) { | 124 if (!file_watcher_.Watch(mtab_path_, watcher_delegate_)) { |
77 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; | 125 LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed"; |
78 return; | 126 return; |
79 } | 127 } |
80 | 128 |
81 UpdateMtab(); | 129 UpdateMtab(); |
82 } | 130 } |
83 | 131 |
84 void MediaDeviceNotificationsLinux::UpdateMtab() { | 132 void MediaDeviceNotificationsLinux::UpdateMtab() { |
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
86 | 134 |
87 MountMap new_mtab; | 135 MountMap new_mtab; |
88 ReadMtab(&new_mtab); | 136 ReadMtab(&new_mtab); |
89 | 137 |
90 // Check existing mtab entries for unaccounted mount points. | 138 // Check existing mtab entries for unaccounted mount points. |
91 // These mount points must have been removed in the new mtab. | 139 // These mount points must have been removed in the new mtab. |
92 MountMap::iterator it = mtab_.begin(); | 140 std::vector<std::string> mount_points_to_erase; |
93 while (it != mtab_.end()) { | 141 for (MountMap::const_iterator it = mtab_.begin(); it != mtab_.end(); ++it) { |
94 const MountPoint& mount_point(it->first); | 142 const std::string& mount_point = it->first; |
95 // |mount_point| not in |new_mtab|. | 143 // |mount_point| not in |new_mtab|. |
96 if (new_mtab.find(mount_point) == new_mtab.end()) { | 144 if (new_mtab.find(mount_point) == new_mtab.end()) { |
97 const SystemMonitor::DeviceIdType& device_id(it->second.second); | 145 const SystemMonitor::DeviceIdType& device_id = it->second.second; |
98 RemoveOldDevice(device_id); | 146 RemoveOldDevice(device_id); |
99 mtab_.erase(it++); | 147 mount_points_to_erase.push_back(mount_point); |
100 continue; | |
101 } | 148 } |
102 // Existing mount point. Ignore and deal in the next loop. | |
103 ++it; | |
104 } | 149 } |
| 150 // Erase the |mtab_| entries afterwards. Erasing in the loop above using the |
| 151 // iterator is slightly more efficient, but more tricky, since calling |
| 152 // std::map::erase() on an iterator invalidates it. |
| 153 for (size_t i = 0; i < mount_points_to_erase.size(); ++i) |
| 154 mtab_.erase(mount_points_to_erase[i]); |
105 | 155 |
106 // Check new mtab entries against existing ones. | 156 // Check new mtab entries against existing ones. |
107 for (MountMap::iterator newiter = new_mtab.begin(); | 157 for (MountMap::iterator newiter = new_mtab.begin(); |
108 newiter != new_mtab.end(); | 158 newiter != new_mtab.end(); |
109 ++newiter) { | 159 ++newiter) { |
110 const MountPoint& mount_point(newiter->first); | 160 const std::string& mount_point = newiter->first; |
111 const MountDeviceAndId& mount_device_and_id(newiter->second); | 161 const MountDeviceAndId& mount_device_and_id = newiter->second; |
112 const MountDevice& mount_device(newiter->second.first); | 162 const std::string& mount_device = mount_device_and_id.first; |
113 SystemMonitor::DeviceIdType& id(newiter->second.second); | 163 SystemMonitor::DeviceIdType& id = newiter->second.second; |
114 MountMap::iterator olditer = mtab_.find(mount_point); | 164 MountMap::iterator olditer = mtab_.find(mount_point); |
115 // Check to see if it is a new mount point. | 165 // Check to see if it is a new mount point. |
116 if (olditer == mtab_.end()) { | 166 if (olditer == mtab_.end()) { |
117 if (IsMediaDevice(mount_point)) { | 167 if (IsMediaDevice(mount_point)) { |
118 AddNewDevice(mount_device, mount_point, &id); | 168 AddNewDevice(mount_device, mount_point, &id); |
119 mtab_[mount_point] = mount_device_and_id; | 169 mtab_.insert(std::make_pair(mount_point, mount_device_and_id)); |
120 } | 170 } |
121 continue; | 171 continue; |
122 } | 172 } |
123 | 173 |
124 // Existing mount point. Check to see if a new device is mounted there. | 174 // Existing mount point. Check to see if a new device is mounted there. |
125 MountDeviceAndId& old_mount_device_and_id(olditer->second); | 175 const MountDeviceAndId& old_mount_device_and_id = olditer->second; |
126 if (mount_device == old_mount_device_and_id.first) | 176 if (mount_device == old_mount_device_and_id.first) |
127 continue; | 177 continue; |
128 | 178 |
129 // New device mounted. | 179 // New device mounted. |
130 RemoveOldDevice(old_mount_device_and_id.second); | 180 RemoveOldDevice(old_mount_device_and_id.second); |
131 if (IsMediaDevice(mount_point)) { | 181 if (IsMediaDevice(mount_point)) { |
132 AddNewDevice(mount_device, mount_point, &id); | 182 AddNewDevice(mount_device, mount_point, &id); |
133 olditer->second = mount_device_and_id; | 183 olditer->second = mount_device_and_id; |
134 } | 184 } |
135 } | 185 } |
136 } | 186 } |
137 | 187 |
138 void MediaDeviceNotificationsLinux::ReadMtab(MountMap* mtab) { | 188 void MediaDeviceNotificationsLinux::ReadMtab(MountMap* mtab) { |
139 FILE* fp = setmntent(mtab_path_.value().c_str(), "r"); | 189 FILE* fp = setmntent(mtab_path_.value().c_str(), "r"); |
140 if (!fp) | 190 if (!fp) |
141 return; | 191 return; |
142 | 192 |
143 MountMap& new_mtab = *mtab; | 193 MountMap& new_mtab = *mtab; |
144 struct mntent entry; | 194 mntent entry; |
145 char buf[512]; | 195 char buf[512]; |
146 SystemMonitor::DeviceIdType mount_position = 0; | 196 SystemMonitor::DeviceIdType mount_position = 0; |
147 typedef std::pair<MountPoint, SystemMonitor::DeviceIdType> MountPointAndId; | 197 typedef std::pair<std::string, SystemMonitor::DeviceIdType> MountPointAndId; |
148 typedef std::map<MountDevice, MountPointAndId> DeviceMap; | 198 typedef std::map<std::string, MountPointAndId> DeviceMap; |
149 DeviceMap device_map; | 199 DeviceMap device_map; |
150 while (getmntent_r(fp, &entry, buf, sizeof(buf))) { | 200 while (getmntent_r(fp, &entry, buf, sizeof(buf))) { |
151 // We only care about real file systems. | 201 // We only care about real file systems. |
152 if (known_file_systems_.find(entry.mnt_type) == known_file_systems_.end()) | 202 if (known_file_systems_.find(entry.mnt_type) == known_file_systems_.end()) |
153 continue; | 203 continue; |
154 // Add entries, but overwrite entries for the same mount device. Keep track | 204 // Add entries, but overwrite entries for the same mount device. Keep track |
155 // of the entry positions in the device id field and use that below to | 205 // of the entry positions in the device id field and use that below to |
156 // resolve multiple devices mounted at the same mount point. | 206 // resolve multiple devices mounted at the same mount point. |
157 device_map[entry.mnt_fsname] = | 207 MountPointAndId mount_point_and_id = |
158 std::make_pair(entry.mnt_dir, mount_position++); | 208 std::make_pair(entry.mnt_dir, mount_position++); |
| 209 DeviceMap::iterator it = device_map.find(entry.mnt_fsname); |
| 210 if (it == device_map.end()) { |
| 211 device_map.insert(it, |
| 212 std::make_pair(entry.mnt_fsname, mount_point_and_id)); |
| 213 } else { |
| 214 it->second = mount_point_and_id; |
| 215 } |
159 } | 216 } |
160 endmntent(fp); | 217 endmntent(fp); |
161 | 218 |
162 for (DeviceMap::iterator device_it = device_map.begin(); | 219 for (DeviceMap::const_iterator device_it = device_map.begin(); |
163 device_it != device_map.end(); | 220 device_it != device_map.end(); |
164 ++device_it) { | 221 ++device_it) { |
165 const MountDevice& device = device_it->first; | 222 const std::string& device = device_it->first; |
166 const MountPoint& mount_point = device_it->second.first; | 223 const std::string& mount_point = device_it->second.first; |
167 const SystemMonitor::DeviceIdType& position = device_it->second.second; | 224 const SystemMonitor::DeviceIdType& position = device_it->second.second; |
168 | 225 |
169 // No device at |mount_point|, save |device| to it. | 226 // No device at |mount_point|, save |device| to it. |
170 MountMap::iterator mount_it = new_mtab.find(mount_point); | 227 MountMap::iterator mount_it = new_mtab.find(mount_point); |
171 if (mount_it == new_mtab.end()) { | 228 if (mount_it == new_mtab.end()) { |
172 new_mtab[mount_point] = std::make_pair(device, position); | 229 new_mtab.insert(std::make_pair(mount_point, |
| 230 std::make_pair(device, position))); |
173 continue; | 231 continue; |
174 } | 232 } |
175 | 233 |
176 // There is already a device mounted at |mount_point|. Check to see if | 234 // There is already a device mounted at |mount_point|. Check to see if |
177 // the existing mount entry is newer than the current one. | 235 // the existing mount entry is newer than the current one. |
178 MountDevice& existing_device = mount_it->second.first; | 236 std::string& existing_device = mount_it->second.first; |
179 SystemMonitor::DeviceIdType& existing_position = mount_it->second.second; | 237 SystemMonitor::DeviceIdType& existing_position = mount_it->second.second; |
180 if (existing_position > position) | 238 if (existing_position > position) |
181 continue; | 239 continue; |
182 | 240 |
183 // The current entry is newer, update the mount point entry. | 241 // The current entry is newer, update the mount point entry. |
184 existing_device = device; | 242 existing_device = device; |
185 existing_position = position; | 243 existing_position = position; |
186 } | 244 } |
187 } | 245 } |
188 | 246 |
189 bool MediaDeviceNotificationsLinux::IsMediaDevice( | 247 bool MediaDeviceNotificationsLinux::IsMediaDevice( |
190 const MountPoint& mount_point) { | 248 const std::string& mount_point) { |
191 FilePath dcim_path(mount_point); | 249 FilePath dcim_path(mount_point); |
192 FilePath::StringType dcim_dir(kDCIMDirName); | 250 FilePath::StringType dcim_dir = kDCIMDirName; |
193 if (!file_util::DirectoryExists(dcim_path.Append(dcim_dir))) { | 251 if (!file_util::DirectoryExists(dcim_path.Append(dcim_dir))) { |
194 // Check for lowercase 'dcim' as well. | 252 // Check for lowercase 'dcim' as well. |
195 FilePath dcim_path_lower(dcim_path.Append(StringToLowerASCII(dcim_dir))); | 253 FilePath dcim_path_lower(dcim_path.Append(StringToLowerASCII(dcim_dir))); |
196 if (!file_util::DirectoryExists(dcim_path_lower)) { | 254 if (!file_util::DirectoryExists(dcim_path_lower)) { |
197 return false; | 255 return false; |
198 } | 256 } |
199 } | 257 } |
200 return true; | 258 return true; |
201 } | 259 } |
202 | 260 |
203 void MediaDeviceNotificationsLinux::AddNewDevice( | 261 void MediaDeviceNotificationsLinux::AddNewDevice( |
204 const MountDevice& mount_device, | 262 const std::string& mount_device, |
205 const MountPoint& mount_point, | 263 const std::string& mount_point, |
206 base::SystemMonitor::DeviceIdType* device_id) { | 264 base::SystemMonitor::DeviceIdType* device_id) { |
207 *device_id = current_device_id_++; | 265 *device_id = current_device_id_++; |
208 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | 266 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); |
209 system_monitor->ProcessMediaDeviceAttached(*device_id, | 267 system_monitor->ProcessMediaDeviceAttached(*device_id, |
210 mount_device, | 268 mount_device, |
211 FilePath(mount_point)); | 269 FilePath(mount_point)); |
212 } | 270 } |
213 | 271 |
214 void MediaDeviceNotificationsLinux::RemoveOldDevice( | 272 void MediaDeviceNotificationsLinux::RemoveOldDevice( |
215 const base::SystemMonitor::DeviceIdType& device_id) { | 273 const base::SystemMonitor::DeviceIdType& device_id) { |
216 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); | 274 base::SystemMonitor* system_monitor = base::SystemMonitor::Get(); |
217 system_monitor->ProcessMediaDeviceDetached(device_id); | 275 system_monitor->ProcessMediaDeviceDetached(device_id); |
218 } | 276 } |
219 | 277 |
220 MediaDeviceNotificationsLinux::WatcherDelegate::WatcherDelegate( | |
221 MediaDeviceNotificationsLinux* notifier) | |
222 : notifier_(notifier) { | |
223 } | |
224 | |
225 MediaDeviceNotificationsLinux::WatcherDelegate::~WatcherDelegate() { | |
226 } | |
227 | |
228 void MediaDeviceNotificationsLinux::WatcherDelegate::OnFilePathChanged( | |
229 const FilePath& path) { | |
230 notifier_->OnFilePathChanged(path); | |
231 } | |
232 | |
233 } // namespace content | 278 } // namespace content |
OLD | NEW |