dom/system/gonk/AutoMounter.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f5012a8da3a7
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

mercurial