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