1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/AutoMounter.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,872 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include <errno.h> 1.9 +#include <fcntl.h> 1.10 +#include <pthread.h> 1.11 +#include <signal.h> 1.12 +#include <string.h> 1.13 +#include <strings.h> 1.14 +#include <unistd.h> 1.15 + 1.16 +#include <arpa/inet.h> 1.17 +#include <linux/types.h> 1.18 +#include <linux/netlink.h> 1.19 +#include <netinet/in.h> 1.20 +#include <sys/socket.h> 1.21 +#include <android/log.h> 1.22 + 1.23 +#include "AutoMounter.h" 1.24 +#include "nsVolumeService.h" 1.25 +#include "AutoMounterSetting.h" 1.26 +#include "base/message_loop.h" 1.27 +#include "mozilla/AutoRestore.h" 1.28 +#include "mozilla/FileUtils.h" 1.29 +#include "mozilla/Hal.h" 1.30 +#include "mozilla/StaticPtr.h" 1.31 +#include "nsAutoPtr.h" 1.32 +#include "nsMemory.h" 1.33 +#include "nsString.h" 1.34 +#include "nsThreadUtils.h" 1.35 +#include "nsXULAppAPI.h" 1.36 +#include "OpenFileFinder.h" 1.37 +#include "Volume.h" 1.38 +#include "VolumeManager.h" 1.39 + 1.40 +using namespace mozilla::hal; 1.41 + 1.42 +/************************************************************************** 1.43 +* 1.44 +* The following "switch" files are available for monitoring usb 1.45 +* connections: 1.46 +* 1.47 +* /sys/devices/virtual/switch/usb_connected/state 1.48 +* /sys/devices/virtual/switch/usb_configuration/state 1.49 +* 1.50 +* Under gingerbread, only the usb_configuration seems to be available. 1.51 +* Starting with honeycomb, usb_connected was also added. 1.52 +* 1.53 +* When a cable insertion/removal occurs, then a uevent similar to the 1.54 +* following will be generted: 1.55 +* 1.56 +* change@/devices/virtual/switch/usb_configuration 1.57 +* ACTION=change 1.58 +* DEVPATH=/devices/virtual/switch/usb_configuration 1.59 +* SUBSYSTEM=switch 1.60 +* SWITCH_NAME=usb_configuration 1.61 +* SWITCH_STATE=0 1.62 +* SEQNUM=5038 1.63 +* 1.64 +* SWITCH_STATE will be 0 after a removal and 1 after an insertion 1.65 +* 1.66 +**************************************************************************/ 1.67 + 1.68 +#define USB_CONFIGURATION_SWITCH_NAME NS_LITERAL_STRING("usb_configuration") 1.69 + 1.70 +#define GB_SYS_UMS_ENABLE "/sys/devices/virtual/usb_composite/usb_mass_storage/enable" 1.71 +#define GB_SYS_USB_CONFIGURED "/sys/devices/virtual/switch/usb_configuration/state" 1.72 + 1.73 +#define ICS_SYS_USB_FUNCTIONS "/sys/devices/virtual/android_usb/android0/functions" 1.74 +#define ICS_SYS_UMS_DIRECTORY "/sys/devices/virtual/android_usb/android0/f_mass_storage" 1.75 +#define ICS_SYS_USB_STATE "/sys/devices/virtual/android_usb/android0/state" 1.76 + 1.77 +#define USE_DEBUG 0 1.78 + 1.79 +#undef LOG 1.80 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter", ## args) 1.81 +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "AutoMounter", ## args) 1.82 +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter", ## args) 1.83 + 1.84 +#if USE_DEBUG 1.85 +#define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "AutoMounter" , ## args) 1.86 +#else 1.87 +#define DBG(args...) 1.88 +#endif 1.89 + 1.90 +namespace mozilla { 1.91 +namespace system { 1.92 + 1.93 +class AutoMounter; 1.94 + 1.95 +static void SetAutoMounterStatus(int32_t aStatus); 1.96 + 1.97 +/***************************************************************************/ 1.98 + 1.99 +inline const char* SwitchStateStr(const SwitchEvent& aEvent) 1.100 +{ 1.101 + return aEvent.status() == SWITCH_STATE_ON ? "plugged" : "unplugged"; 1.102 +} 1.103 + 1.104 +/***************************************************************************/ 1.105 + 1.106 +static bool 1.107 +IsUsbCablePluggedIn() 1.108 +{ 1.109 +#if 0 1.110 + // Use this code when bug 745078 gets fixed (or use whatever the 1.111 + // appropriate method is) 1.112 + return GetCurrentSwitchEvent(SWITCH_USB) == SWITCH_STATE_ON; 1.113 +#else 1.114 + // Until then, just go read the file directly 1.115 + if (access(ICS_SYS_USB_STATE, F_OK) == 0) { 1.116 + char usbState[20]; 1.117 + if (ReadSysFile(ICS_SYS_USB_STATE, usbState, sizeof(usbState))) { 1.118 + return strcmp(usbState, "CONFIGURED") == 0; 1.119 + } 1.120 + ERR("Error reading file '%s': %s", ICS_SYS_USB_STATE, strerror(errno)); 1.121 + return false; 1.122 + } 1.123 + bool configured; 1.124 + if (ReadSysFile(GB_SYS_USB_CONFIGURED, &configured)) { 1.125 + return configured; 1.126 + } 1.127 + ERR("Error reading file '%s': %s", GB_SYS_USB_CONFIGURED, strerror(errno)); 1.128 + return false; 1.129 +#endif 1.130 +} 1.131 + 1.132 +/***************************************************************************/ 1.133 + 1.134 +// The AutoVolumeManagerStateObserver allows the AutoMounter to know when 1.135 +// the volume manager changes state (i.e. it has finished initialization) 1.136 +class AutoVolumeManagerStateObserver : public VolumeManager::StateObserver 1.137 +{ 1.138 +public: 1.139 + virtual void Notify(const VolumeManager::StateChangedEvent& aEvent); 1.140 +}; 1.141 + 1.142 +// The AutoVolumeEventObserver allows the AutoMounter to know about card 1.143 +// insertion and removal, as well as state changes in the volume. 1.144 +class AutoVolumeEventObserver : public Volume::EventObserver 1.145 +{ 1.146 +public: 1.147 + virtual void Notify(Volume * const & aEvent); 1.148 +}; 1.149 + 1.150 +class AutoMounterResponseCallback : public VolumeResponseCallback 1.151 +{ 1.152 +public: 1.153 + AutoMounterResponseCallback() 1.154 + : mErrorCount(0) 1.155 + { 1.156 + } 1.157 + 1.158 +protected: 1.159 + virtual void ResponseReceived(const VolumeCommand* aCommand); 1.160 + 1.161 +private: 1.162 + const static int kMaxErrorCount = 3; // Max number of errors before we give up 1.163 + 1.164 + int mErrorCount; 1.165 +}; 1.166 + 1.167 +/***************************************************************************/ 1.168 + 1.169 +class AutoMounter 1.170 +{ 1.171 +public: 1.172 + NS_INLINE_DECL_REFCOUNTING(AutoMounter) 1.173 + 1.174 + typedef nsTArray<RefPtr<Volume>> VolumeArray; 1.175 + 1.176 + AutoMounter() 1.177 + : mResponseCallback(new AutoMounterResponseCallback), 1.178 + mMode(AUTOMOUNTER_DISABLE) 1.179 + { 1.180 + VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver); 1.181 + Volume::RegisterObserver(&mVolumeEventObserver); 1.182 + 1.183 + VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); 1.184 + VolumeManager::VolumeArray::index_type i; 1.185 + for (i = 0; i < numVolumes; i++) { 1.186 + RefPtr<Volume> vol = VolumeManager::GetVolume(i); 1.187 + if (vol) { 1.188 + vol->RegisterObserver(&mVolumeEventObserver); 1.189 + // We need to pick up the intial value of the 1.190 + // ums.volume.NAME.enabled setting. 1.191 + AutoMounterSetting::CheckVolumeSettings(vol->Name()); 1.192 + } 1.193 + } 1.194 + 1.195 + DBG("Calling UpdateState from constructor"); 1.196 + UpdateState(); 1.197 + } 1.198 + 1.199 + ~AutoMounter() 1.200 + { 1.201 + VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); 1.202 + VolumeManager::VolumeArray::index_type volIndex; 1.203 + for (volIndex = 0; volIndex < numVolumes; volIndex++) { 1.204 + RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); 1.205 + if (vol) { 1.206 + vol->UnregisterObserver(&mVolumeEventObserver); 1.207 + } 1.208 + } 1.209 + Volume::UnregisterObserver(&mVolumeEventObserver); 1.210 + VolumeManager::UnregisterStateObserver(&mVolumeManagerStateObserver); 1.211 + } 1.212 + 1.213 + void UpdateState(); 1.214 + 1.215 + const char* ModeStr(int32_t aMode) 1.216 + { 1.217 + switch (aMode) { 1.218 + case AUTOMOUNTER_DISABLE: return "Disable"; 1.219 + case AUTOMOUNTER_ENABLE: return "Enable"; 1.220 + case AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED: return "DisableWhenUnplugged"; 1.221 + } 1.222 + return "??? Unknown ???"; 1.223 + } 1.224 + 1.225 + void SetMode(int32_t aMode) 1.226 + { 1.227 + if ((aMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && 1.228 + (mMode == AUTOMOUNTER_DISABLE)) { 1.229 + // If it's already disabled, then leave it as disabled. 1.230 + // AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED implies "enabled until unplugged" 1.231 + aMode = AUTOMOUNTER_DISABLE; 1.232 + } 1.233 + 1.234 + if ((aMode == AUTOMOUNTER_DISABLE) && 1.235 + (mMode == AUTOMOUNTER_ENABLE) && IsUsbCablePluggedIn()) { 1.236 + // On many devices (esp non-Samsung), we can't force the disable, so we 1.237 + // need to defer until the USB cable is actually unplugged. 1.238 + // See bug 777043. 1.239 + // 1.240 + // Otherwise our attempt to disable it will fail, and we'll wind up in a bad 1.241 + // state where the AutoMounter thinks that Sharing has been turned off, but 1.242 + // the files are actually still being Shared because the attempt to unshare 1.243 + // failed. 1.244 + LOG("Attempting to disable UMS. Deferring until USB cable is unplugged."); 1.245 + aMode = AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED; 1.246 + } 1.247 + 1.248 + if (aMode != mMode) { 1.249 + LOG("Changing mode from '%s' to '%s'", ModeStr(mMode), ModeStr(aMode)); 1.250 + mMode = aMode; 1.251 + DBG("Calling UpdateState due to mode set to %d", mMode); 1.252 + UpdateState(); 1.253 + } 1.254 + } 1.255 + 1.256 + void SetSharingMode(const nsACString& aVolumeName, bool aAllowSharing) 1.257 + { 1.258 + RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); 1.259 + if (!vol) { 1.260 + return; 1.261 + } 1.262 + if (vol->IsSharingEnabled() == aAllowSharing) { 1.263 + return; 1.264 + } 1.265 + vol->SetUnmountRequested(false); 1.266 + vol->SetMountRequested(false); 1.267 + vol->SetSharingEnabled(aAllowSharing); 1.268 + DBG("Calling UpdateState due to volume %s shareing set to %d", 1.269 + vol->NameStr(), (int)aAllowSharing); 1.270 + UpdateState(); 1.271 + } 1.272 + 1.273 + void FormatVolume(const nsACString& aVolumeName) 1.274 + { 1.275 + RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); 1.276 + if (!vol) { 1.277 + return; 1.278 + } 1.279 + if (vol->IsFormatRequested()) { 1.280 + return; 1.281 + } 1.282 + vol->SetUnmountRequested(false); 1.283 + vol->SetMountRequested(false); 1.284 + vol->SetFormatRequested(true); 1.285 + DBG("Calling UpdateState due to volume %s formatting set to %d", 1.286 + vol->NameStr(), (int)vol->IsFormatRequested()); 1.287 + UpdateState(); 1.288 + } 1.289 + 1.290 + void MountVolume(const nsACString& aVolumeName) 1.291 + { 1.292 + RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); 1.293 + if (!vol) { 1.294 + return; 1.295 + } 1.296 + vol->SetUnmountRequested(false); 1.297 + if (vol->IsMountRequested() || vol->mState == nsIVolume::STATE_MOUNTED) { 1.298 + return; 1.299 + } 1.300 + vol->SetMountRequested(true); 1.301 + DBG("Calling UpdateState due to volume %s mounting set to %d", 1.302 + vol->NameStr(), (int)vol->IsMountRequested()); 1.303 + UpdateState(); 1.304 + } 1.305 + 1.306 + void UnmountVolume(const nsACString& aVolumeName) 1.307 + { 1.308 + RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName); 1.309 + if (!vol) { 1.310 + return; 1.311 + } 1.312 + if (vol->IsUnmountRequested()) { 1.313 + return; 1.314 + } 1.315 + vol->SetMountRequested(false); 1.316 + vol->SetUnmountRequested(true); 1.317 + DBG("Calling UpdateState due to volume %s unmounting set to %d", 1.318 + vol->NameStr(), (int)vol->IsUnmountRequested()); 1.319 + UpdateState(); 1.320 + } 1.321 + 1.322 +private: 1.323 + 1.324 + AutoVolumeEventObserver mVolumeEventObserver; 1.325 + AutoVolumeManagerStateObserver mVolumeManagerStateObserver; 1.326 + RefPtr<VolumeResponseCallback> mResponseCallback; 1.327 + int32_t mMode; 1.328 +}; 1.329 + 1.330 +static StaticRefPtr<AutoMounter> sAutoMounter; 1.331 + 1.332 +/***************************************************************************/ 1.333 + 1.334 +void 1.335 +AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &) 1.336 +{ 1.337 + LOG("VolumeManager state changed event: %s", VolumeManager::StateStr()); 1.338 + 1.339 + if (!sAutoMounter) { 1.340 + return; 1.341 + } 1.342 + DBG("Calling UpdateState due to VolumeManagerStateObserver"); 1.343 + sAutoMounter->UpdateState(); 1.344 +} 1.345 + 1.346 +void 1.347 +AutoVolumeEventObserver::Notify(Volume * const &) 1.348 +{ 1.349 + if (!sAutoMounter) { 1.350 + return; 1.351 + } 1.352 + DBG("Calling UpdateState due to VolumeEventStateObserver"); 1.353 + sAutoMounter->UpdateState(); 1.354 +} 1.355 + 1.356 +void 1.357 +AutoMounterResponseCallback::ResponseReceived(const VolumeCommand* aCommand) 1.358 +{ 1.359 + 1.360 + if (WasSuccessful()) { 1.361 + DBG("Calling UpdateState due to Volume::OnSuccess"); 1.362 + mErrorCount = 0; 1.363 + sAutoMounter->UpdateState(); 1.364 + return; 1.365 + } 1.366 + ERR("Command '%s' failed: %d '%s'", 1.367 + aCommand->CmdStr(), ResponseCode(), ResponseStr().get()); 1.368 + 1.369 + if (++mErrorCount < kMaxErrorCount) { 1.370 + DBG("Calling UpdateState due to VolumeResponseCallback::OnError"); 1.371 + sAutoMounter->UpdateState(); 1.372 + } 1.373 +} 1.374 + 1.375 +/***************************************************************************/ 1.376 + 1.377 +void 1.378 +AutoMounter::UpdateState() 1.379 +{ 1.380 + static bool inUpdateState = false; 1.381 + if (inUpdateState) { 1.382 + // When UpdateState calls SetISharing, this causes a volume state 1.383 + // change to occur, which would normally cause UpdateState to be called 1.384 + // again. We want the volume state change to go out (so that device 1.385 + // storage will see the change in sharing state), but since we're 1.386 + // already in UpdateState we just want to prevent recursion from screwing 1.387 + // things up. 1.388 + return; 1.389 + } 1.390 + AutoRestore<bool> inUpdateStateDetector(inUpdateState); 1.391 + inUpdateState = true; 1.392 + 1.393 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.394 + 1.395 + // If the following preconditions are met: 1.396 + // - UMS is available (i.e. compiled into the kernel) 1.397 + // - UMS is enabled 1.398 + // - AutoMounter is enabled 1.399 + // - USB cable is plugged in 1.400 + // then we will try to unmount and share 1.401 + // otherwise we will try to unshare and mount. 1.402 + 1.403 + if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { 1.404 + // The volume manager isn't in a ready state, so there 1.405 + // isn't anything else that we can do. 1.406 + LOG("UpdateState: VolumeManager not ready yet"); 1.407 + return; 1.408 + } 1.409 + 1.410 + if (mResponseCallback->IsPending()) { 1.411 + // We only deal with one outstanding volume command at a time, 1.412 + // so we need to wait for it to finish. 1.413 + return; 1.414 + } 1.415 + 1.416 + bool umsAvail = false; 1.417 + bool umsEnabled = false; 1.418 + 1.419 + if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) { 1.420 + umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0); 1.421 + if (umsAvail) { 1.422 + char functionsStr[60]; 1.423 + if (ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) { 1.424 + umsEnabled = strstr(functionsStr, "mass_storage") != nullptr; 1.425 + } else { 1.426 + ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno)); 1.427 + umsEnabled = false; 1.428 + } 1.429 + } else { 1.430 + umsEnabled = false; 1.431 + } 1.432 + } else { 1.433 + umsAvail = ReadSysFile(GB_SYS_UMS_ENABLE, &umsEnabled); 1.434 + } 1.435 + 1.436 + bool usbCablePluggedIn = IsUsbCablePluggedIn(); 1.437 + bool enabled = (mMode == AUTOMOUNTER_ENABLE); 1.438 + 1.439 + if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) { 1.440 + enabled = usbCablePluggedIn; 1.441 + if (!usbCablePluggedIn) { 1.442 + mMode = AUTOMOUNTER_DISABLE; 1.443 + } 1.444 + } 1.445 + 1.446 + bool tryToShare = (umsAvail && umsEnabled && enabled && usbCablePluggedIn); 1.447 + LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d", 1.448 + umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare); 1.449 + 1.450 + bool filesOpen = false; 1.451 + static unsigned filesOpenDelayCount = 0; 1.452 + VolumeArray::index_type volIndex; 1.453 + VolumeArray::size_type numVolumes = VolumeManager::NumVolumes(); 1.454 + for (volIndex = 0; volIndex < numVolumes; volIndex++) { 1.455 + RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex); 1.456 + Volume::STATE volState = vol->State(); 1.457 + 1.458 + if (vol->State() == nsIVolume::STATE_MOUNTED) { 1.459 + LOG("UpdateState: Volume %s is %s and %s @ %s gen %d locked %d sharing %c", 1.460 + vol->NameStr(), vol->StateStr(), 1.461 + vol->MediaPresent() ? "inserted" : "missing", 1.462 + vol->MountPoint().get(), vol->MountGeneration(), 1.463 + (int)vol->IsMountLocked(), 1.464 + vol->CanBeShared() ? (vol->IsSharingEnabled() ? 'y' : 'n') : 'x'); 1.465 + } else { 1.466 + LOG("UpdateState: Volume %s is %s and %s", vol->NameStr(), vol->StateStr(), 1.467 + vol->MediaPresent() ? "inserted" : "missing"); 1.468 + } 1.469 + if (!vol->MediaPresent()) { 1.470 + // No media - nothing we can do 1.471 + continue; 1.472 + } 1.473 + 1.474 + if ((tryToShare && vol->IsSharingEnabled()) || 1.475 + vol->IsFormatRequested() || 1.476 + vol->IsUnmountRequested()) { 1.477 + switch (volState) { 1.478 + // We're going to try to unmount the volume 1.479 + case nsIVolume::STATE_MOUNTED: { 1.480 + if (vol->IsMountLocked()) { 1.481 + // The volume is currently locked, so leave it in the mounted 1.482 + // state. 1.483 + LOGW("UpdateState: Mounted volume %s is locked, not sharing or formatting", 1.484 + vol->NameStr()); 1.485 + break; 1.486 + } 1.487 + 1.488 + // Mark the volume as if we've started sharing. This will cause 1.489 + // apps which watch device storage notifications to see the volume 1.490 + // go into the shared state, and prompt them to close any open files 1.491 + // that they might have. 1.492 + if (tryToShare && vol->IsSharingEnabled()) { 1.493 + vol->SetIsSharing(true); 1.494 + } else if (vol->IsFormatRequested()){ 1.495 + vol->SetIsFormatting(true); 1.496 + } 1.497 + 1.498 + // Check to see if there are any open files on the volume and 1.499 + // don't initiate the unmount while there are open files. 1.500 + OpenFileFinder::Info fileInfo; 1.501 + OpenFileFinder fileFinder(vol->MountPoint()); 1.502 + if (fileFinder.First(&fileInfo)) { 1.503 + LOGW("The following files are open under '%s'", 1.504 + vol->MountPoint().get()); 1.505 + do { 1.506 + LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", 1.507 + fileInfo.mPid, 1.508 + fileInfo.mFileName.get(), 1.509 + fileInfo.mAppName.get(), 1.510 + fileInfo.mComm.get(), 1.511 + fileInfo.mExe.get()); 1.512 + } while (fileFinder.Next(&fileInfo)); 1.513 + LOGW("UpdateState: Mounted volume %s has open files, not sharing or formatting", 1.514 + vol->NameStr()); 1.515 + 1.516 + // Check again in a few seconds to see if the files are closed. 1.517 + // Since we're trying to share the volume, this implies that we're 1.518 + // plugged into the PC via USB and this in turn implies that the 1.519 + // battery is charging, so we don't need to be too concerned about 1.520 + // wasting battery here. 1.521 + // 1.522 + // If we just detected that there were files open, then we use 1.523 + // a short timer. We will have told the apps that we're trying 1.524 + // trying to share, and they'll be closing their files. This makes 1.525 + // the sharing more responsive. If after a few seconds, the apps 1.526 + // haven't closed their files, then we back off. 1.527 + 1.528 + int delay = 1000; 1.529 + if (filesOpenDelayCount > 10) { 1.530 + delay = 5000; 1.531 + } 1.532 + MessageLoopForIO::current()-> 1.533 + PostDelayedTask(FROM_HERE, 1.534 + NewRunnableMethod(this, &AutoMounter::UpdateState), 1.535 + delay); 1.536 + filesOpen = true; 1.537 + break; 1.538 + } 1.539 + 1.540 + // Volume is mounted, we need to unmount before 1.541 + // we can share. 1.542 + LOG("UpdateState: Unmounting %s", vol->NameStr()); 1.543 + vol->StartUnmount(mResponseCallback); 1.544 + return; // UpdateState will be called again when the Unmount command completes 1.545 + } 1.546 + case nsIVolume::STATE_IDLE: { 1.547 + LOG("UpdateState: Volume %s is nsIVolume::STATE_IDLE", vol->NameStr()); 1.548 + if (vol->IsFormatting() && !vol->IsFormatRequested()) { 1.549 + vol->SetFormatRequested(false); 1.550 + LOG("UpdateState: Mounting %s", vol->NameStr()); 1.551 + vol->StartMount(mResponseCallback); 1.552 + break; 1.553 + } 1.554 + if (tryToShare && vol->IsSharingEnabled()) { 1.555 + // Volume is unmounted. We can go ahead and share. 1.556 + LOG("UpdateState: Sharing %s", vol->NameStr()); 1.557 + vol->StartShare(mResponseCallback); 1.558 + } else if (vol->IsFormatRequested()){ 1.559 + // Volume is unmounted. We can go ahead and format. 1.560 + LOG("UpdateState: Formatting %s", vol->NameStr()); 1.561 + vol->StartFormat(mResponseCallback); 1.562 + } 1.563 + return; // UpdateState will be called again when the Share/Format command completes 1.564 + } 1.565 + default: { 1.566 + // Not in a state that we can do anything about. 1.567 + break; 1.568 + } 1.569 + } 1.570 + } else { 1.571 + // We're going to try and unshare and remount the volumes 1.572 + switch (volState) { 1.573 + case nsIVolume::STATE_SHARED: { 1.574 + // Volume is shared. We can go ahead and unshare. 1.575 + LOG("UpdateState: Unsharing %s", vol->NameStr()); 1.576 + vol->StartUnshare(mResponseCallback); 1.577 + return; // UpdateState will be called again when the Unshare command completes 1.578 + } 1.579 + case nsIVolume::STATE_IDLE: { 1.580 + if (!vol->IsUnmountRequested()) { 1.581 + // Volume is unmounted and mount-requested, try to mount. 1.582 + 1.583 + LOG("UpdateState: Mounting %s", vol->NameStr()); 1.584 + vol->StartMount(mResponseCallback); 1.585 + } 1.586 + return; // UpdateState will be called again when Mount command completes 1.587 + } 1.588 + default: { 1.589 + // Not in a state that we can do anything about. 1.590 + break; 1.591 + } 1.592 + } 1.593 + } 1.594 + } 1.595 + 1.596 + int32_t status = AUTOMOUNTER_STATUS_DISABLED; 1.597 + if (filesOpen) { 1.598 + filesOpenDelayCount++; 1.599 + status = AUTOMOUNTER_STATUS_FILES_OPEN; 1.600 + } else if (enabled) { 1.601 + filesOpenDelayCount = 0; 1.602 + status = AUTOMOUNTER_STATUS_ENABLED; 1.603 + } 1.604 + SetAutoMounterStatus(status); 1.605 +} 1.606 + 1.607 +/***************************************************************************/ 1.608 + 1.609 +static void 1.610 +InitAutoMounterIOThread() 1.611 +{ 1.612 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.613 + MOZ_ASSERT(!sAutoMounter); 1.614 + 1.615 + sAutoMounter = new AutoMounter(); 1.616 +} 1.617 + 1.618 +static void 1.619 +ShutdownAutoMounterIOThread() 1.620 +{ 1.621 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.622 + 1.623 + sAutoMounter = nullptr; 1.624 + ShutdownVolumeManager(); 1.625 +} 1.626 + 1.627 +static void 1.628 +SetAutoMounterModeIOThread(const int32_t& aMode) 1.629 +{ 1.630 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.631 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.632 + MOZ_ASSERT(sAutoMounter); 1.633 + 1.634 + sAutoMounter->SetMode(aMode); 1.635 +} 1.636 + 1.637 +static void 1.638 +SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAllowSharing) 1.639 +{ 1.640 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.641 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.642 + MOZ_ASSERT(sAutoMounter); 1.643 + 1.644 + sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing); 1.645 +} 1.646 + 1.647 +static void 1.648 +AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName) 1.649 +{ 1.650 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.651 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.652 + MOZ_ASSERT(sAutoMounter); 1.653 + 1.654 + sAutoMounter->FormatVolume(aVolumeName); 1.655 +} 1.656 + 1.657 +static void 1.658 +AutoMounterMountVolumeIOThread(const nsCString& aVolumeName) 1.659 +{ 1.660 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.661 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.662 + MOZ_ASSERT(sAutoMounter); 1.663 + 1.664 + sAutoMounter->MountVolume(aVolumeName); 1.665 +} 1.666 + 1.667 +static void 1.668 +AutoMounterUnmountVolumeIOThread(const nsCString& aVolumeName) 1.669 +{ 1.670 + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 1.671 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.672 + MOZ_ASSERT(sAutoMounter); 1.673 + 1.674 + sAutoMounter->UnmountVolume(aVolumeName); 1.675 +} 1.676 + 1.677 +static void 1.678 +UsbCableEventIOThread() 1.679 +{ 1.680 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.681 + 1.682 + if (!sAutoMounter) { 1.683 + return; 1.684 + } 1.685 + DBG("Calling UpdateState due to USBCableEvent"); 1.686 + sAutoMounter->UpdateState(); 1.687 +} 1.688 + 1.689 +/************************************************************************** 1.690 +* 1.691 +* Public API 1.692 +* 1.693 +* Since the AutoMounter runs in IO Thread context, we need to switch 1.694 +* to IOThread context before we can do anything. 1.695 +* 1.696 +**************************************************************************/ 1.697 + 1.698 +class UsbCableObserver MOZ_FINAL : public SwitchObserver 1.699 +{ 1.700 + ~UsbCableObserver() 1.701 + { 1.702 + UnregisterSwitchObserver(SWITCH_USB, this); 1.703 + } 1.704 + 1.705 +public: 1.706 + NS_INLINE_DECL_REFCOUNTING(UsbCableObserver) 1.707 + 1.708 + UsbCableObserver() 1.709 + { 1.710 + RegisterSwitchObserver(SWITCH_USB, this); 1.711 + } 1.712 + 1.713 + virtual void Notify(const SwitchEvent& aEvent) 1.714 + { 1.715 + DBG("UsbCable switch device: %d state: %s\n", 1.716 + aEvent.device(), SwitchStateStr(aEvent)); 1.717 + XRE_GetIOMessageLoop()->PostTask( 1.718 + FROM_HERE, 1.719 + NewRunnableFunction(UsbCableEventIOThread)); 1.720 + } 1.721 +}; 1.722 + 1.723 +static StaticRefPtr<UsbCableObserver> sUsbCableObserver; 1.724 +static StaticRefPtr<AutoMounterSetting> sAutoMounterSetting; 1.725 + 1.726 +static void 1.727 +InitVolumeConfig() 1.728 +{ 1.729 + // This function uses /system/etc/volume.cfg to add additional volumes 1.730 + // to the Volume Manager. 1.731 + // 1.732 + // This is useful on devices like the Nexus 4, which have no physical sd card 1.733 + // or dedicated partition. 1.734 + // 1.735 + // The format of the volume.cfg file is as follows: 1.736 + // create volume-name mount-point 1.737 + // Blank lines and lines starting with the hash character "#" will be ignored. 1.738 + 1.739 + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); 1.740 + NS_ENSURE_TRUE_VOID(vs); 1.741 + 1.742 + ScopedCloseFile fp; 1.743 + int n = 0; 1.744 + char line[255]; 1.745 + char *command, *vol_name_cstr, *mount_point_cstr, *save_ptr; 1.746 + const char *filename = "/system/etc/volume.cfg"; 1.747 + if (!(fp = fopen(filename, "r"))) { 1.748 + LOG("Unable to open volume configuration file '%s' - ignoring", filename); 1.749 + return; 1.750 + } 1.751 + while(fgets(line, sizeof(line), fp)) { 1.752 + const char *delim = " \t\n"; 1.753 + n++; 1.754 + 1.755 + if (line[0] == '#') 1.756 + continue; 1.757 + if (!(command = strtok_r(line, delim, &save_ptr))) { 1.758 + // Blank line - ignore 1.759 + continue; 1.760 + } 1.761 + if (!strcmp(command, "create")) { 1.762 + if (!(vol_name_cstr = strtok_r(nullptr, delim, &save_ptr))) { 1.763 + ERR("No vol_name in %s line %d", filename, n); 1.764 + continue; 1.765 + } 1.766 + if (!(mount_point_cstr = strtok_r(nullptr, delim, &save_ptr))) { 1.767 + ERR("No mount point for volume '%s'. %s line %d", vol_name_cstr, filename, n); 1.768 + continue; 1.769 + } 1.770 + nsString mount_point = NS_ConvertUTF8toUTF16(mount_point_cstr); 1.771 + nsString vol_name = NS_ConvertUTF8toUTF16(vol_name_cstr); 1.772 + nsresult rv; 1.773 + rv = vs->CreateFakeVolume(vol_name, mount_point); 1.774 + NS_ENSURE_SUCCESS_VOID(rv); 1.775 + rv = vs->SetFakeVolumeState(vol_name, nsIVolume::STATE_MOUNTED); 1.776 + NS_ENSURE_SUCCESS_VOID(rv); 1.777 + } 1.778 + else { 1.779 + ERR("Unrecognized command: '%s'", command); 1.780 + } 1.781 + } 1.782 +} 1.783 + 1.784 +void 1.785 +InitAutoMounter() 1.786 +{ 1.787 + InitVolumeConfig(); 1.788 + InitVolumeManager(); 1.789 + sAutoMounterSetting = new AutoMounterSetting(); 1.790 + 1.791 + XRE_GetIOMessageLoop()->PostTask( 1.792 + FROM_HERE, 1.793 + NewRunnableFunction(InitAutoMounterIOThread)); 1.794 + 1.795 + // Switch Observers need to run on the main thread, so we need to 1.796 + // start it here and have it send events to the AutoMounter running 1.797 + // on the IO Thread. 1.798 + sUsbCableObserver = new UsbCableObserver(); 1.799 +} 1.800 + 1.801 +int32_t 1.802 +GetAutoMounterStatus() 1.803 +{ 1.804 + if (sAutoMounterSetting) { 1.805 + return sAutoMounterSetting->GetStatus(); 1.806 + } 1.807 + return AUTOMOUNTER_STATUS_DISABLED; 1.808 +} 1.809 + 1.810 +//static 1.811 +void 1.812 +SetAutoMounterStatus(int32_t aStatus) 1.813 +{ 1.814 + if (sAutoMounterSetting) { 1.815 + sAutoMounterSetting->SetStatus(aStatus); 1.816 + } 1.817 +} 1.818 + 1.819 +void 1.820 +SetAutoMounterMode(int32_t aMode) 1.821 +{ 1.822 + XRE_GetIOMessageLoop()->PostTask( 1.823 + FROM_HERE, 1.824 + NewRunnableFunction(SetAutoMounterModeIOThread, aMode)); 1.825 +} 1.826 + 1.827 +void 1.828 +SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing) 1.829 +{ 1.830 + XRE_GetIOMessageLoop()->PostTask( 1.831 + FROM_HERE, 1.832 + NewRunnableFunction(SetAutoMounterSharingModeIOThread, 1.833 + aVolumeName, aAllowSharing)); 1.834 +} 1.835 + 1.836 +void 1.837 +AutoMounterFormatVolume(const nsCString& aVolumeName) 1.838 +{ 1.839 + XRE_GetIOMessageLoop()->PostTask( 1.840 + FROM_HERE, 1.841 + NewRunnableFunction(AutoMounterFormatVolumeIOThread, 1.842 + aVolumeName)); 1.843 +} 1.844 + 1.845 +void 1.846 +AutoMounterMountVolume(const nsCString& aVolumeName) 1.847 +{ 1.848 + XRE_GetIOMessageLoop()->PostTask( 1.849 + FROM_HERE, 1.850 + NewRunnableFunction(AutoMounterMountVolumeIOThread, 1.851 + aVolumeName)); 1.852 +} 1.853 + 1.854 +void 1.855 +AutoMounterUnmountVolume(const nsCString& aVolumeName) 1.856 +{ 1.857 + XRE_GetIOMessageLoop()->PostTask( 1.858 + FROM_HERE, 1.859 + NewRunnableFunction(AutoMounterUnmountVolumeIOThread, 1.860 + aVolumeName)); 1.861 +} 1.862 + 1.863 +void 1.864 +ShutdownAutoMounter() 1.865 +{ 1.866 + sAutoMounterSetting = nullptr; 1.867 + sUsbCableObserver = nullptr; 1.868 + 1.869 + XRE_GetIOMessageLoop()->PostTask( 1.870 + FROM_HERE, 1.871 + NewRunnableFunction(ShutdownAutoMounterIOThread)); 1.872 +} 1.873 + 1.874 +} // system 1.875 +} // mozilla