|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include <errno.h> |
|
6 #include <fcntl.h> |
|
7 #include <pthread.h> |
|
8 #include <signal.h> |
|
9 #include <string.h> |
|
10 #include <strings.h> |
|
11 #include <unistd.h> |
|
12 |
|
13 #include <arpa/inet.h> |
|
14 #include <linux/types.h> |
|
15 #include <linux/netlink.h> |
|
16 #include <netinet/in.h> |
|
17 #include <sys/socket.h> |
|
18 #include <android/log.h> |
|
19 |
|
20 #include "AutoMounter.h" |
|
21 #include "nsVolumeService.h" |
|
22 #include "AutoMounterSetting.h" |
|
23 #include "base/message_loop.h" |
|
24 #include "mozilla/AutoRestore.h" |
|
25 #include "mozilla/FileUtils.h" |
|
26 #include "mozilla/Hal.h" |
|
27 #include "mozilla/StaticPtr.h" |
|
28 #include "nsAutoPtr.h" |
|
29 #include "nsMemory.h" |
|
30 #include "nsString.h" |
|
31 #include "nsThreadUtils.h" |
|
32 #include "nsXULAppAPI.h" |
|
33 #include "OpenFileFinder.h" |
|
34 #include "Volume.h" |
|
35 #include "VolumeManager.h" |
|
36 |
|
37 using namespace mozilla::hal; |
|
38 |
|
39 /************************************************************************** |
|
40 * |
|
41 * The following "switch" files are available for monitoring usb |
|
42 * connections: |
|
43 * |
|
44 * /sys/devices/virtual/switch/usb_connected/state |
|
45 * /sys/devices/virtual/switch/usb_configuration/state |
|
46 * |
|
47 * Under gingerbread, only the usb_configuration seems to be available. |
|
48 * Starting with honeycomb, usb_connected was also added. |
|
49 * |
|
50 * When a cable insertion/removal occurs, then a uevent similar to the |
|
51 * following will be generted: |
|
52 * |
|
53 * change@/devices/virtual/switch/usb_configuration |
|
54 * ACTION=change |
|
55 * DEVPATH=/devices/virtual/switch/usb_configuration |
|
56 * SUBSYSTEM=switch |
|
57 * SWITCH_NAME=usb_configuration |
|
58 * SWITCH_STATE=0 |
|
59 * SEQNUM=5038 |
|
60 * |
|
61 * SWITCH_STATE will be 0 after a removal and 1 after an insertion |
|
62 * |
|
63 **************************************************************************/ |
|
64 |
|
65 #define USB_CONFIGURATION_SWITCH_NAME NS_LITERAL_STRING("usb_configuration") |
|
66 |
|
67 #define GB_SYS_UMS_ENABLE "/sys/devices/virtual/usb_composite/usb_mass_storage/enable" |
|
68 #define GB_SYS_USB_CONFIGURED "/sys/devices/virtual/switch/usb_configuration/state" |
|
69 |
|
70 #define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions" |
|
71 #define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage" |
|
72 #define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state" |
|
73 |
|
74 #define USE_DEBUG 0 |
|
75 |
|
76 #undef LOG |
|
77 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter", ## args) |
|
78 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "AutoMounter", ## args) |
|
79 #define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter", ## args) |
|
80 |
|
81 #if USE_DEBUG |
|
82 #define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "AutoMounter" , ## args) |
|
83 #else |
|
84 #define DBG(args...) |
|
85 #endif |
|
86 |
|
87 namespace mozilla { |
|
88 namespace system { |
|
89 |
|
90 class AutoMounter; |
|
91 |
|
92 static void SetAutoMounterStatus(int32_t aStatus); |
|
93 |
|
94 /***************************************************************************/ |
|
95 |
|
96 inline const char* SwitchStateStr(const SwitchEvent& aEvent) |
|
97 { |
|
98 return aEvent.status() == SWITCH_STATE_ON ? "plugged" : "unplugged"; |
|
99 } |
|
100 |
|
101 /***************************************************************************/ |
|
102 |
|
103 static bool |
|
104 IsUsbCablePluggedIn() |
|
105 { |
|
106 #if 0 |
|
107 // Use this code when bug 745078 gets fixed (or use whatever the |
|
108 // appropriate method is) |
|
109 return GetCurrentSwitchEvent(SWITCH_USB) == SWITCH_STATE_ON; |
|
110 #else |
|
111 // Until then, just go read the file directly |
|
112 if (access(ICS_SYS_USB_STATE, F_OK) == 0) { |
|
113 char usbState[20]; |
|
114 if (ReadSysFile(ICS_SYS_USB_STATE, usbState, sizeof(usbState))) { |
|
115 return strcmp(usbState, "CONFIGURED") == 0; |
|
116 } |
|
117 ERR("Error reading file '%s': %s", ICS_SYS_USB_STATE, strerror(errno)); |
|
118 return false; |
|
119 } |
|
120 bool configured; |
|
121 if (ReadSysFile(GB_SYS_USB_CONFIGURED, &configured)) { |
|
122 return configured; |
|
123 } |
|
124 ERR("Error reading file '%s': %s", GB_SYS_USB_CONFIGURED, strerror(errno)); |
|
125 return false; |
|
126 #endif |
|
127 } |
|
128 |
|
129 /***************************************************************************/ |
|
130 |
|
131 // The AutoVolumeManagerStateObserver allows the AutoMounter to know when |
|
132 // the volume manager changes state (i.e. it has finished initialization) |
|
133 class AutoVolumeManagerStateObserver : public VolumeManager::StateObserver |
|
134 { |
|
135 public: |
|
136 virtual void Notify(const VolumeManager::StateChangedEvent& aEvent); |
|
137 }; |
|
138 |
|
139 // The AutoVolumeEventObserver allows the AutoMounter to know about card |
|
140 // insertion and removal, as well as state changes in the volume. |
|
141 class AutoVolumeEventObserver : public Volume::EventObserver |
|
142 { |
|
143 public: |
|
144 virtual void Notify(Volume * const & aEvent); |
|
145 }; |
|
146 |
|
147 class AutoMounterResponseCallback : public VolumeResponseCallback |
|
148 { |
|
149 public: |
|
150 AutoMounterResponseCallback() |
|
151 : mErrorCount(0) |
|
152 { |
|
153 } |
|
154 |
|
155 protected: |
|
156 virtual void ResponseReceived(const VolumeCommand* aCommand); |
|
157 |
|
158 private: |
|
159 const static int kMaxErrorCount = 3; // Max number of errors before we give up |
|
160 |
|
161 int mErrorCount; |
|
162 }; |
|
163 |
|
164 /***************************************************************************/ |
|
165 |
|
166 class AutoMounter |
|
167 { |
|
168 public: |
|
169 NS_INLINE_DECL_REFCOUNTING(AutoMounter) |
|
170 |
|
171 typedef nsTArray<RefPtr<Volume>> VolumeArray; |
|
172 |
|
173 AutoMounter() |
|
174 : mResponseCallback(new AutoMounterResponseCallback), |
|
175 mMode(AUTOMOUNTER_DISABLE) |
|
176 { |
|
177 VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver); |
|
178 Volume::RegisterObserver(&mVolumeEventObserver); |
|
179 |
|
180 VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); |
|
181 VolumeManager::VolumeArray::index_type i; |
|
182 for (i = 0; i < numVolumes; i++) { |
|
183 RefPtr<Volume> vol = VolumeManager::GetVolume(i); |
|
184 if (vol) { |
|
185 vol->RegisterObserver(&mVolumeEventObserver); |
|
186 // We need to pick up the intial value of the |
|
187 // ums.volume.NAME.enabled setting. |
|
188 AutoMounterSetting::CheckVolumeSettings(vol->Name()); |
|
189 } |
|
190 } |
|
191 |
|
192 DBG("Calling UpdateState from constructor"); |
|
193 UpdateState(); |
|
194 } |
|
195 |
|
196 ~AutoMounter() |
|
197 { |
|
198 VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); |
|
199 VolumeManager::VolumeArray::index_type volIndex; |
|
200 for (volIndex = 0; volIndex < numVolumes; volIndex++) { |
|
201 RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); |
|
202 if (vol) { |
|
203 vol->UnregisterObserver(&mVolumeEventObserver); |
|
204 } |
|
205 } |
|
206 Volume::UnregisterObserver(&mVolumeEventObserver); |
|
207 VolumeManager::UnregisterStateObserver(&mVolumeManagerStateObserver); |
|
208 } |
|
209 |
|
210 void UpdateState(); |
|
211 |
|
212 const char* ModeStr(int32_t aMode) |
|
213 { |
|
214 switch (aMode) { |
|
215 case AUTOMOUNTER_DISABLE: return "Disable"; |
|
216 case AUTOMOUNTER_ENABLE: return "Enable"; |
|
217 case AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED: return "DisableWhenUnplugged"; |
|
218 } |
|
219 return "??? Unknown ???"; |
|
220 } |
|
221 |
|
222 void SetMode(int32_t aMode) |
|
223 { |
|
224 if ((aMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && |
|
225 (mMode == AUTOMOUNTER_DISABLE)) { |
|
226 // If it's already disabled, then leave it as disabled. |
|
227 // AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED implies "enabled until unplugged" |
|
228 aMode = AUTOMOUNTER_DISABLE; |
|
229 } |
|
230 |
|
231 if ((aMode == AUTOMOUNTER_DISABLE) && |
|
232 (mMode == AUTOMOUNTER_ENABLE) && IsUsbCablePluggedIn()) { |
|
233 // On many devices (esp non-Samsung), we can't force the disable, so we |
|
234 // need to defer until the USB cable is actually unplugged. |
|
235 // See bug 777043. |
|
236 // |
|
237 // Otherwise our attempt to disable it will fail, and we'll wind up in a bad |
|
238 // state where the AutoMounter thinks that Sharing has been turned off, but |
|
239 // the files are actually still being Shared because the attempt to unshare |
|
240 // failed. |
|
241 LOG("Attempting to disable UMS. Deferring until USB cable is unplugged."); |
|
242 aMode = AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED; |
|
243 } |
|
244 |
|
245 if (aMode != mMode) { |
|
246 LOG("Changing mode from '%s' to '%s'", ModeStr(mMode), ModeStr(aMode)); |
|
247 mMode = aMode; |
|
248 DBG("Calling UpdateState due to mode set to %d", mMode); |
|
249 UpdateState(); |
|
250 } |
|
251 } |
|
252 |
|
253 void SetSharingMode(const nsACString& aVolumeName, bool aAllowSharing) |
|
254 { |
|
255 RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); |
|
256 if (!vol) { |
|
257 return; |
|
258 } |
|
259 if (vol->IsSharingEnabled() == aAllowSharing) { |
|
260 return; |
|
261 } |
|
262 vol->SetUnmountRequested(false); |
|
263 vol->SetMountRequested(false); |
|
264 vol->SetSharingEnabled(aAllowSharing); |
|
265 DBG("Calling UpdateState due to volume %s shareing set to %d", |
|
266 vol->NameStr(), (int)aAllowSharing); |
|
267 UpdateState(); |
|
268 } |
|
269 |
|
270 void FormatVolume(const nsACString& aVolumeName) |
|
271 { |
|
272 RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); |
|
273 if (!vol) { |
|
274 return; |
|
275 } |
|
276 if (vol->IsFormatRequested()) { |
|
277 return; |
|
278 } |
|
279 vol->SetUnmountRequested(false); |
|
280 vol->SetMountRequested(false); |
|
281 vol->SetFormatRequested(true); |
|
282 DBG("Calling UpdateState due to volume %s formatting set to %d", |
|
283 vol->NameStr(), (int)vol->IsFormatRequested()); |
|
284 UpdateState(); |
|
285 } |
|
286 |
|
287 void MountVolume(const nsACString& aVolumeName) |
|
288 { |
|
289 RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); |
|
290 if (!vol) { |
|
291 return; |
|
292 } |
|
293 vol->SetUnmountRequested(false); |
|
294 if (vol->IsMountRequested() || vol->mState == nsIVolume::STATE_MOUNTED) { |
|
295 return; |
|
296 } |
|
297 vol->SetMountRequested(true); |
|
298 DBG("Calling UpdateState due to volume %s mounting set to %d", |
|
299 vol->NameStr(), (int)vol->IsMountRequested()); |
|
300 UpdateState(); |
|
301 } |
|
302 |
|
303 void UnmountVolume(const nsACString& aVolumeName) |
|
304 { |
|
305 RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); |
|
306 if (!vol) { |
|
307 return; |
|
308 } |
|
309 if (vol->IsUnmountRequested()) { |
|
310 return; |
|
311 } |
|
312 vol->SetMountRequested(false); |
|
313 vol->SetUnmountRequested(true); |
|
314 DBG("Calling UpdateState due to volume %s unmounting set to %d", |
|
315 vol->NameStr(), (int)vol->IsUnmountRequested()); |
|
316 UpdateState(); |
|
317 } |
|
318 |
|
319 private: |
|
320 |
|
321 AutoVolumeEventObserver mVolumeEventObserver; |
|
322 AutoVolumeManagerStateObserver mVolumeManagerStateObserver; |
|
323 RefPtr<VolumeResponseCallback> mResponseCallback; |
|
324 int32_t mMode; |
|
325 }; |
|
326 |
|
327 static StaticRefPtr<AutoMounter> sAutoMounter; |
|
328 |
|
329 /***************************************************************************/ |
|
330 |
|
331 void |
|
332 AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &) |
|
333 { |
|
334 LOG("VolumeManager state changed event: %s", VolumeManager::StateStr()); |
|
335 |
|
336 if (!sAutoMounter) { |
|
337 return; |
|
338 } |
|
339 DBG("Calling UpdateState due to VolumeManagerStateObserver"); |
|
340 sAutoMounter->UpdateState(); |
|
341 } |
|
342 |
|
343 void |
|
344 AutoVolumeEventObserver::Notify(Volume * const &) |
|
345 { |
|
346 if (!sAutoMounter) { |
|
347 return; |
|
348 } |
|
349 DBG("Calling UpdateState due to VolumeEventStateObserver"); |
|
350 sAutoMounter->UpdateState(); |
|
351 } |
|
352 |
|
353 void |
|
354 AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand) |
|
355 { |
|
356 |
|
357 if (WasSuccessful()) { |
|
358 DBG("Calling UpdateState due to Volume::OnSuccess"); |
|
359 mErrorCount = 0; |
|
360 sAutoMounter->UpdateState(); |
|
361 return; |
|
362 } |
|
363 ERR("Command '%s' failed: %d '%s'", |
|
364 aCommand->CmdStr(), ResponseCode(), ResponseStr().get()); |
|
365 |
|
366 if (++mErrorCount < kMaxErrorCount) { |
|
367 DBG("Calling UpdateState due to VolumeResponseCallback::OnError"); |
|
368 sAutoMounter->UpdateState(); |
|
369 } |
|
370 } |
|
371 |
|
372 /***************************************************************************/ |
|
373 |
|
374 void |
|
375 AutoMounter::UpdateState() |
|
376 { |
|
377 static bool inUpdateState = false; |
|
378 if (inUpdateState) { |
|
379 // When UpdateState calls SetISharing, this causes a volume state |
|
380 // change to occur, which would normally cause UpdateState to be called |
|
381 // again. We want the volume state change to go out (so that device |
|
382 // storage will see the change in sharing state), but since we're |
|
383 // already in UpdateState we just want to prevent recursion from screwing |
|
384 // things up. |
|
385 return; |
|
386 } |
|
387 AutoRestore<bool> inUpdateStateDetector(inUpdateState); |
|
388 inUpdateState = true; |
|
389 |
|
390 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
391 |
|
392 // If the following preconditions are met: |
|
393 // - UMS is available (i.e. compiled into the kernel) |
|
394 // - UMS is enabled |
|
395 // - AutoMounter is enabled |
|
396 // - USB cable is plugged in |
|
397 // then we will try to unmount and share |
|
398 // otherwise we will try to unshare and mount. |
|
399 |
|
400 if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { |
|
401 // The volume manager isn't in a ready state, so there |
|
402 // isn't anything else that we can do. |
|
403 LOG("UpdateState: VolumeManager not ready yet"); |
|
404 return; |
|
405 } |
|
406 |
|
407 if (mResponseCallback->IsPending()) { |
|
408 // We only deal with one outstanding volume command at a time, |
|
409 // so we need to wait for it to finish. |
|
410 return; |
|
411 } |
|
412 |
|
413 bool umsAvail = false; |
|
414 bool umsEnabled = false; |
|
415 |
|
416 if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) { |
|
417 umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0); |
|
418 if (umsAvail) { |
|
419 char functionsStr[60]; |
|
420 if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) { |
|
421 umsEnabled = strstr(functionsStr, "mass_storage") != nullptr; |
|
422 } else { |
|
423 ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno)); |
|
424 umsEnabled = false; |
|
425 } |
|
426 } else { |
|
427 umsEnabled = false; |
|
428 } |
|
429 } else { |
|
430 umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled); |
|
431 } |
|
432 |
|
433 bool usbCablePluggedIn = IsUsbCablePluggedIn(); |
|
434 bool enabled = (mMode == AUTOMOUNTER_ENABLE); |
|
435 |
|
436 if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) { |
|
437 enabled = usbCablePluggedIn; |
|
438 if (!usbCablePluggedIn) { |
|
439 mMode = AUTOMOUNTER_DISABLE; |
|
440 } |
|
441 } |
|
442 |
|
443 bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn); |
|
444 LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d", |
|
445 umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare); |
|
446 |
|
447 bool filesOpen = false; |
|
448 static unsigned filesOpenDelayCount = 0; |
|
449 VolumeArray::index_type volIndex; |
|
450 VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); |
|
451 for (volIndex = 0; volIndex < numVolumes; volIndex++) { |
|
452 RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); |
|
453 Volume::STATE volState = vol->State(); |
|
454 |
|
455 if (vol->State() == nsIVolume::STATE_MOUNTED) { |
|
456 LOG("UpdateState: Volume %s is %s and %s @ %s gen %d locked %d sharing %c", |
|
457 vol->NameStr(), vol->StateStr(), |
|
458 vol->MediaPresent() ? "inserted" : "missing", |
|
459 vol->MountPoint().get(), vol->MountGeneration(), |
|
460 (int)vol->IsMountLocked(), |
|
461 vol->CanBeShared() ? (vol->IsSharingEnabled() ? 'y' : 'n') : 'x'); |
|
462 } else { |
|
463 LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(), |
|
464 vol->MediaPresent() ? "inserted" : "missing"); |
|
465 } |
|
466 if (!vol->MediaPresent()) { |
|
467 // No media - nothing we can do |
|
468 continue; |
|
469 } |
|
470 |
|
471 if ((tryToShare && vol->IsSharingEnabled()) || |
|
472 vol->IsFormatRequested() || |
|
473 vol->IsUnmountRequested()) { |
|
474 switch (volState) { |
|
475 // We're going to try to unmount the volume |
|
476 case nsIVolume::STATE_MOUNTED: { |
|
477 if (vol->IsMountLocked()) { |
|
478 // The volume is currently locked, so leave it in the mounted |
|
479 // state. |
|
480 LOGW("UpdateState: Mounted volume %s is locked, not sharing or formatting", |
|
481 vol->NameStr()); |
|
482 break; |
|
483 } |
|
484 |
|
485 // Mark the volume as if we've started sharing. This will cause |
|
486 // apps which watch device storage notifications to see the volume |
|
487 // go into the shared state, and prompt them to close any open files |
|
488 // that they might have. |
|
489 if (tryToShare && vol->IsSharingEnabled()) { |
|
490 vol->SetIsSharing(true); |
|
491 } else if (vol->IsFormatRequested()){ |
|
492 vol->SetIsFormatting(true); |
|
493 } |
|
494 |
|
495 // Check to see if there are any open files on the volume and |
|
496 // don't initiate the unmount while there are open files. |
|
497 OpenFileFinder::Info fileInfo; |
|
498 OpenFileFinder fileFinder(vol->MountPoint()); |
|
499 if (fileFinder.First(&fileInfo)) { |
|
500 LOGW("The following files are open under '%s'", |
|
501 vol->MountPoint().get()); |
|
502 do { |
|
503 LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", |
|
504 fileInfo.mPid, |
|
505 fileInfo.mFileName.get(), |
|
506 fileInfo.mAppName.get(), |
|
507 fileInfo.mComm.get(), |
|
508 fileInfo.mExe.get()); |
|
509 } while (fileFinder.Next(&fileInfo)); |
|
510 LOGW("UpdateState: Mounted volume %s has open files, not sharing or formatting", |
|
511 vol->NameStr()); |
|
512 |
|
513 // Check again in a few seconds to see if the files are closed. |
|
514 // Since we're trying to share the volume, this implies that we're |
|
515 // plugged into the PC via USB and this in turn implies that the |
|
516 // battery is charging, so we don't need to be too concerned about |
|
517 // wasting battery here. |
|
518 // |
|
519 // If we just detected that there were files open, then we use |
|
520 // a short timer. We will have told the apps that we're trying |
|
521 // trying to share, and they'll be closing their files. This makes |
|
522 // the sharing more responsive. If after a few seconds, the apps |
|
523 // haven't closed their files, then we back off. |
|
524 |
|
525 int delay = 1000; |
|
526 if (filesOpenDelayCount > 10) { |
|
527 delay = 5000; |
|
528 } |
|
529 MessageLoopForIO::current()-> |
|
530 PostDelayedTask(FROM_HERE, |
|
531 NewRunnableMethod(this, &AutoMounter::UpdateState), |
|
532 delay); |
|
533 filesOpen = true; |
|
534 break; |
|
535 } |
|
536 |
|
537 // Volume is mounted, we need to unmount before |
|
538 // we can share. |
|
539 LOG("UpdateState: Unmounting %s", vol->NameStr()); |
|
540 vol->StartUnmount(mResponseCallback); |
|
541 return; // UpdateState will be called again when the Unmount command completes |
|
542 } |
|
543 case nsIVolume::STATE_IDLE: { |
|
544 LOG("UpdateState: Volume %s is nsIVolume::STATE_IDLE", vol->NameStr()); |
|
545 if (vol->IsFormatting() && !vol->IsFormatRequested()) { |
|
546 vol->SetFormatRequested(false); |
|
547 LOG("UpdateState: Mounting %s", vol->NameStr()); |
|
548 vol->StartMount(mResponseCallback); |
|
549 break; |
|
550 } |
|
551 if (tryToShare && vol->IsSharingEnabled()) { |
|
552 // Volume is unmounted. We can go ahead and share. |
|
553 LOG("UpdateState: Sharing %s", vol->NameStr()); |
|
554 vol->StartShare(mResponseCallback); |
|
555 } else if (vol->IsFormatRequested()){ |
|
556 // Volume is unmounted. We can go ahead and format. |
|
557 LOG("UpdateState: Formatting %s", vol->NameStr()); |
|
558 vol->StartFormat(mResponseCallback); |
|
559 } |
|
560 return; // UpdateState will be called again when the Share/Format command completes |
|
561 } |
|
562 default: { |
|
563 // Not in a state that we can do anything about. |
|
564 break; |
|
565 } |
|
566 } |
|
567 } else { |
|
568 // We're going to try and unshare and remount the volumes |
|
569 switch (volState) { |
|
570 case nsIVolume::STATE_SHARED: { |
|
571 // Volume is shared. We can go ahead and unshare. |
|
572 LOG("UpdateState: Unsharing %s", vol->NameStr()); |
|
573 vol->StartUnshare(mResponseCallback); |
|
574 return; // UpdateState will be called again when the Unshare command completes |
|
575 } |
|
576 case nsIVolume::STATE_IDLE: { |
|
577 if (!vol->IsUnmountRequested()) { |
|
578 // Volume is unmounted and mount-requested, try to mount. |
|
579 |
|
580 LOG("UpdateState: Mounting %s", vol->NameStr()); |
|
581 vol->StartMount(mResponseCallback); |
|
582 } |
|
583 return; // UpdateState will be called again when Mount command completes |
|
584 } |
|
585 default: { |
|
586 // Not in a state that we can do anything about. |
|
587 break; |
|
588 } |
|
589 } |
|
590 } |
|
591 } |
|
592 |
|
593 int32_t status = AUTOMOUNTER_STATUS_DISABLED; |
|
594 if (filesOpen) { |
|
595 filesOpenDelayCount++; |
|
596 status = AUTOMOUNTER_STATUS_FILES_OPEN; |
|
597 } else if (enabled) { |
|
598 filesOpenDelayCount = 0; |
|
599 status = AUTOMOUNTER_STATUS_ENABLED; |
|
600 } |
|
601 SetAutoMounterStatus(status); |
|
602 } |
|
603 |
|
604 /***************************************************************************/ |
|
605 |
|
606 static void |
|
607 InitAutoMounterIOThread() |
|
608 { |
|
609 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
610 MOZ_ASSERT(!sAutoMounter); |
|
611 |
|
612 sAutoMounter = new AutoMounter(); |
|
613 } |
|
614 |
|
615 static void |
|
616 ShutdownAutoMounterIOThread() |
|
617 { |
|
618 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
619 |
|
620 sAutoMounter = nullptr; |
|
621 ShutdownVolumeManager(); |
|
622 } |
|
623 |
|
624 static void |
|
625 SetAutoMounterModeIOThread(const int32_t& aMode) |
|
626 { |
|
627 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
628 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
629 MOZ_ASSERT(sAutoMounter); |
|
630 |
|
631 sAutoMounter->SetMode(aMode); |
|
632 } |
|
633 |
|
634 static void |
|
635 SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAllowSharing) |
|
636 { |
|
637 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
638 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
639 MOZ_ASSERT(sAutoMounter); |
|
640 |
|
641 sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing); |
|
642 } |
|
643 |
|
644 static void |
|
645 AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName) |
|
646 { |
|
647 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
648 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
649 MOZ_ASSERT(sAutoMounter); |
|
650 |
|
651 sAutoMounter->FormatVolume(aVolumeName); |
|
652 } |
|
653 |
|
654 static void |
|
655 AutoMounterMountVolumeIOThread(const nsCString& aVolumeName) |
|
656 { |
|
657 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
658 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
659 MOZ_ASSERT(sAutoMounter); |
|
660 |
|
661 sAutoMounter->MountVolume(aVolumeName); |
|
662 } |
|
663 |
|
664 static void |
|
665 AutoMounterUnmountVolumeIOThread(const nsCString& aVolumeName) |
|
666 { |
|
667 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
668 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
669 MOZ_ASSERT(sAutoMounter); |
|
670 |
|
671 sAutoMounter->UnmountVolume(aVolumeName); |
|
672 } |
|
673 |
|
674 static void |
|
675 UsbCableEventIOThread() |
|
676 { |
|
677 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
678 |
|
679 if (!sAutoMounter) { |
|
680 return; |
|
681 } |
|
682 DBG("Calling UpdateState due to USBCableEvent"); |
|
683 sAutoMounter->UpdateState(); |
|
684 } |
|
685 |
|
686 /************************************************************************** |
|
687 * |
|
688 * Public API |
|
689 * |
|
690 * Since the AutoMounter runs in IO Thread context, we need to switch |
|
691 * to IOThread context before we can do anything. |
|
692 * |
|
693 **************************************************************************/ |
|
694 |
|
695 class UsbCableObserver MOZ_FINAL : public SwitchObserver |
|
696 { |
|
697 ~UsbCableObserver() |
|
698 { |
|
699 UnregisterSwitchObserver(SWITCH_USB, this); |
|
700 } |
|
701 |
|
702 public: |
|
703 NS_INLINE_DECL_REFCOUNTING(UsbCableObserver) |
|
704 |
|
705 UsbCableObserver() |
|
706 { |
|
707 RegisterSwitchObserver(SWITCH_USB, this); |
|
708 } |
|
709 |
|
710 virtual void Notify(const SwitchEvent& aEvent) |
|
711 { |
|
712 DBG("UsbCable switch device: %d state: %s\n", |
|
713 aEvent.device(), SwitchStateStr(aEvent)); |
|
714 XRE_GetIOMessageLoop()->PostTask( |
|
715 FROM_HERE, |
|
716 NewRunnableFunction(UsbCableEventIOThread)); |
|
717 } |
|
718 }; |
|
719 |
|
720 static StaticRefPtr<UsbCableObserver> sUsbCableObserver; |
|
721 static StaticRefPtr<AutoMounterSetting> sAutoMounterSetting; |
|
722 |
|
723 static void |
|
724 InitVolumeConfig() |
|
725 { |
|
726 // This function uses /system/etc/volume.cfg to add additional volumes |
|
727 // to the Volume Manager. |
|
728 // |
|
729 // This is useful on devices like the Nexus 4, which have no physical sd card |
|
730 // or dedicated partition. |
|
731 // |
|
732 // The format of the volume.cfg file is as follows: |
|
733 // create volume-name mount-point |
|
734 // Blank lines and lines starting with the hash character "#" will be ignored. |
|
735 |
|
736 nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); |
|
737 NS_ENSURE_TRUE_VOID(vs); |
|
738 |
|
739 ScopedCloseFile fp; |
|
740 int n = 0; |
|
741 char line[255]; |
|
742 char *command, *vol_name_cstr, *mount_point_cstr, *save_ptr; |
|
743 const char *filename = "/system/etc/volume.cfg"; |
|
744 if (!(fp = fopen(filename, "r"))) { |
|
745 LOG("Unable to open volume configuration file '%s' - ignoring", filename); |
|
746 return; |
|
747 } |
|
748 while(fgets(line, sizeof(line), fp)) { |
|
749 const char *delim = " \t\n"; |
|
750 n++; |
|
751 |
|
752 if (line[0] == '#') |
|
753 continue; |
|
754 if (!(command = strtok_r(line, delim, &save_ptr))) { |
|
755 // Blank line - ignore |
|
756 continue; |
|
757 } |
|
758 if (!strcmp(command, "create")) { |
|
759 if (!(vol_name_cstr = strtok_r(nullptr, delim, &save_ptr))) { |
|
760 ERR("No vol_name in %s line %d", filename, n); |
|
761 continue; |
|
762 } |
|
763 if (!(mount_point_cstr = strtok_r(nullptr, delim, &save_ptr))) { |
|
764 ERR("No mount point for volume '%s'. %s line %d", vol_name_cstr, filename, n); |
|
765 continue; |
|
766 } |
|
767 nsString mount_point = NS_ConvertUTF8toUTF16(mount_point_cstr); |
|
768 nsString vol_name = NS_ConvertUTF8toUTF16(vol_name_cstr); |
|
769 nsresult rv; |
|
770 rv = vs->CreateFakeVolume(vol_name, mount_point); |
|
771 NS_ENSURE_SUCCESS_VOID(rv); |
|
772 rv = vs->SetFakeVolumeState(vol_name, nsIVolume::STATE_MOUNTED); |
|
773 NS_ENSURE_SUCCESS_VOID(rv); |
|
774 } |
|
775 else { |
|
776 ERR("Unrecognized command: '%s'", command); |
|
777 } |
|
778 } |
|
779 } |
|
780 |
|
781 void |
|
782 InitAutoMounter() |
|
783 { |
|
784 InitVolumeConfig(); |
|
785 InitVolumeManager(); |
|
786 sAutoMounterSetting = new AutoMounterSetting(); |
|
787 |
|
788 XRE_GetIOMessageLoop()->PostTask( |
|
789 FROM_HERE, |
|
790 NewRunnableFunction(InitAutoMounterIOThread)); |
|
791 |
|
792 // Switch Observers need to run on the main thread, so we need to |
|
793 // start it here and have it send events to the AutoMounter running |
|
794 // on the IO Thread. |
|
795 sUsbCableObserver = new UsbCableObserver(); |
|
796 } |
|
797 |
|
798 int32_t |
|
799 GetAutoMounterStatus() |
|
800 { |
|
801 if (sAutoMounterSetting) { |
|
802 return sAutoMounterSetting->GetStatus(); |
|
803 } |
|
804 return AUTOMOUNTER_STATUS_DISABLED; |
|
805 } |
|
806 |
|
807 //static |
|
808 void |
|
809 SetAutoMounterStatus(int32_t aStatus) |
|
810 { |
|
811 if (sAutoMounterSetting) { |
|
812 sAutoMounterSetting->SetStatus(aStatus); |
|
813 } |
|
814 } |
|
815 |
|
816 void |
|
817 SetAutoMounterMode(int32_t aMode) |
|
818 { |
|
819 XRE_GetIOMessageLoop()->PostTask( |
|
820 FROM_HERE, |
|
821 NewRunnableFunction(SetAutoMounterModeIOThread, aMode)); |
|
822 } |
|
823 |
|
824 void |
|
825 SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing) |
|
826 { |
|
827 XRE_GetIOMessageLoop()->PostTask( |
|
828 FROM_HERE, |
|
829 NewRunnableFunction(SetAutoMounterSharingModeIOThread, |
|
830 aVolumeName, aAllowSharing)); |
|
831 } |
|
832 |
|
833 void |
|
834 AutoMounterFormatVolume(const nsCString& aVolumeName) |
|
835 { |
|
836 XRE_GetIOMessageLoop()->PostTask( |
|
837 FROM_HERE, |
|
838 NewRunnableFunction(AutoMounterFormatVolumeIOThread, |
|
839 aVolumeName)); |
|
840 } |
|
841 |
|
842 void |
|
843 AutoMounterMountVolume(const nsCString& aVolumeName) |
|
844 { |
|
845 XRE_GetIOMessageLoop()->PostTask( |
|
846 FROM_HERE, |
|
847 NewRunnableFunction(AutoMounterMountVolumeIOThread, |
|
848 aVolumeName)); |
|
849 } |
|
850 |
|
851 void |
|
852 AutoMounterUnmountVolume(const nsCString& aVolumeName) |
|
853 { |
|
854 XRE_GetIOMessageLoop()->PostTask( |
|
855 FROM_HERE, |
|
856 NewRunnableFunction(AutoMounterUnmountVolumeIOThread, |
|
857 aVolumeName)); |
|
858 } |
|
859 |
|
860 void |
|
861 ShutdownAutoMounter() |
|
862 { |
|
863 sAutoMounterSetting = nullptr; |
|
864 sUsbCableObserver = nullptr; |
|
865 |
|
866 XRE_GetIOMessageLoop()->PostTask( |
|
867 FROM_HERE, |
|
868 NewRunnableFunction(ShutdownAutoMounterIOThread)); |
|
869 } |
|
870 |
|
871 } // system |
|
872 } // mozilla |