dom/system/gonk/AutoMounter.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     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>
    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>
    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"
    37 using namespace mozilla::hal;
    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 **************************************************************************/
    65 #define USB_CONFIGURATION_SWITCH_NAME   NS_LITERAL_STRING("usb_configuration")
    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"
    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"
    74 #define USE_DEBUG 0
    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)
    81 #if USE_DEBUG
    82 #define DBG(args...)  __android_log_print(ANDROID_LOG_DEBUG, "AutoMounter" , ## args)
    83 #else
    84 #define DBG(args...)
    85 #endif
    87 namespace mozilla {
    88 namespace system {
    90 class AutoMounter;
    92 static void SetAutoMounterStatus(int32_t aStatus);
    94 /***************************************************************************/
    96 inline const char* SwitchStateStr(const SwitchEvent& aEvent)
    97 {
    98   return aEvent.status() == SWITCH_STATE_ON ? "plugged" : "unplugged";
    99 }
   101 /***************************************************************************/
   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 }
   129 /***************************************************************************/
   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 };
   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 };
   147 class AutoMounterResponseCallback : public VolumeResponseCallback
   148 {
   149 public:
   150   AutoMounterResponseCallback()
   151     : mErrorCount(0)
   152   {
   153   }
   155 protected:
   156   virtual void ResponseReceived(const VolumeCommand* aCommand);
   158 private:
   159     const static int kMaxErrorCount = 3; // Max number of errors before we give up
   161     int   mErrorCount;
   162 };
   164 /***************************************************************************/
   166 class AutoMounter
   167 {
   168 public:
   169   NS_INLINE_DECL_REFCOUNTING(AutoMounter)
   171   typedef nsTArray<RefPtr<Volume>> VolumeArray;
   173   AutoMounter()
   174     : mResponseCallback(new AutoMounterResponseCallback),
   175       mMode(AUTOMOUNTER_DISABLE)
   176   {
   177     VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver);
   178     Volume::RegisterObserver(&mVolumeEventObserver);
   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     }
   192     DBG("Calling UpdateState from constructor");
   193     UpdateState();
   194   }
   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   }
   210   void UpdateState();
   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   }
   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     }
   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     }
   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   }
   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   }
   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   }
   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   }
   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   }
   319 private:
   321   AutoVolumeEventObserver         mVolumeEventObserver;
   322   AutoVolumeManagerStateObserver  mVolumeManagerStateObserver;
   323   RefPtr<VolumeResponseCallback>  mResponseCallback;
   324   int32_t                         mMode;
   325 };
   327 static StaticRefPtr<AutoMounter> sAutoMounter;
   329 /***************************************************************************/
   331 void
   332 AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &)
   333 {
   334   LOG("VolumeManager state changed event: %s", VolumeManager::StateStr());
   336   if (!sAutoMounter) {
   337     return;
   338   }
   339   DBG("Calling UpdateState due to VolumeManagerStateObserver");
   340   sAutoMounter->UpdateState();
   341 }
   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 }
   353 void
   354 AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand)
   355 {
   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());
   366   if (++mErrorCount < kMaxErrorCount) {
   367     DBG("Calling UpdateState due to VolumeResponseCallback::OnError");
   368     sAutoMounter->UpdateState();
   369   }
   370 }
   372 /***************************************************************************/
   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;
   390   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   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.
   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   }
   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   }
   413   bool  umsAvail = false;
   414   bool  umsEnabled = false;
   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   }
   433   bool usbCablePluggedIn = IsUsbCablePluggedIn();
   434   bool enabled = (mMode == AUTOMOUNTER_ENABLE);
   436   if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) {
   437     enabled = usbCablePluggedIn;
   438     if (!usbCablePluggedIn) {
   439       mMode = AUTOMOUNTER_DISABLE;
   440     }
   441   }
   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);
   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();
   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     }
   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           }
   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           }
   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());
   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.
   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           }
   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.
   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   }
   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 }
   604 /***************************************************************************/
   606 static void
   607 InitAutoMounterIOThread()
   608 {
   609   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   610   MOZ_ASSERT(!sAutoMounter);
   612   sAutoMounter = new AutoMounter();
   613 }
   615 static void
   616 ShutdownAutoMounterIOThread()
   617 {
   618   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   620   sAutoMounter = nullptr;
   621   ShutdownVolumeManager();
   622 }
   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);
   631   sAutoMounter->SetMode(aMode);
   632 }
   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);
   641   sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing);
   642 }
   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);
   651   sAutoMounter->FormatVolume(aVolumeName);
   652 }
   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);
   661   sAutoMounter->MountVolume(aVolumeName);
   662 }
   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);
   671   sAutoMounter->UnmountVolume(aVolumeName);
   672 }
   674 static void
   675 UsbCableEventIOThread()
   676 {
   677   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   679   if (!sAutoMounter) {
   680     return;
   681   }
   682   DBG("Calling UpdateState due to USBCableEvent");
   683   sAutoMounter->UpdateState();
   684 }
   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 **************************************************************************/
   695 class UsbCableObserver MOZ_FINAL : public SwitchObserver
   696 {
   697   ~UsbCableObserver()
   698   {
   699     UnregisterSwitchObserver(SWITCH_USB, this);
   700   }
   702 public:
   703   NS_INLINE_DECL_REFCOUNTING(UsbCableObserver)
   705   UsbCableObserver()
   706   {
   707     RegisterSwitchObserver(SWITCH_USB, this);
   708   }
   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 };
   720 static StaticRefPtr<UsbCableObserver> sUsbCableObserver;
   721 static StaticRefPtr<AutoMounterSetting> sAutoMounterSetting;
   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.
   736   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   737   NS_ENSURE_TRUE_VOID(vs);
   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++;
   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 }
   781 void
   782 InitAutoMounter()
   783 {
   784   InitVolumeConfig();
   785   InitVolumeManager();
   786   sAutoMounterSetting = new AutoMounterSetting();
   788   XRE_GetIOMessageLoop()->PostTask(
   789       FROM_HERE,
   790       NewRunnableFunction(InitAutoMounterIOThread));
   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 }
   798 int32_t
   799 GetAutoMounterStatus()
   800 {
   801   if (sAutoMounterSetting) {
   802     return sAutoMounterSetting->GetStatus();
   803   }
   804   return AUTOMOUNTER_STATUS_DISABLED;
   805 }
   807 //static
   808 void
   809 SetAutoMounterStatus(int32_t aStatus)
   810 {
   811   if (sAutoMounterSetting) {
   812     sAutoMounterSetting->SetStatus(aStatus);
   813   }
   814 }
   816 void
   817 SetAutoMounterMode(int32_t aMode)
   818 {
   819   XRE_GetIOMessageLoop()->PostTask(
   820       FROM_HERE,
   821       NewRunnableFunction(SetAutoMounterModeIOThread, aMode));
   822 }
   824 void
   825 SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing)
   826 {
   827   XRE_GetIOMessageLoop()->PostTask(
   828       FROM_HERE,
   829       NewRunnableFunction(SetAutoMounterSharingModeIOThread,
   830                           aVolumeName, aAllowSharing));
   831 }
   833 void
   834 AutoMounterFormatVolume(const nsCString& aVolumeName)
   835 {
   836   XRE_GetIOMessageLoop()->PostTask(
   837       FROM_HERE,
   838       NewRunnableFunction(AutoMounterFormatVolumeIOThread,
   839                           aVolumeName));
   840 }
   842 void
   843 AutoMounterMountVolume(const nsCString& aVolumeName)
   844 {
   845   XRE_GetIOMessageLoop()->PostTask(
   846       FROM_HERE,
   847       NewRunnableFunction(AutoMounterMountVolumeIOThread,
   848                           aVolumeName));
   849 }
   851 void
   852 AutoMounterUnmountVolume(const nsCString& aVolumeName)
   853 {
   854   XRE_GetIOMessageLoop()->PostTask(
   855       FROM_HERE,
   856       NewRunnableFunction(AutoMounterUnmountVolumeIOThread,
   857                           aVolumeName));
   858 }
   860 void
   861 ShutdownAutoMounter()
   862 {
   863   sAutoMounterSetting = nullptr;
   864   sUsbCableObserver = nullptr;
   866   XRE_GetIOMessageLoop()->PostTask(
   867       FROM_HERE,
   868       NewRunnableFunction(ShutdownAutoMounterIOThread));
   869 }
   871 } // system
   872 } // mozilla

mercurial