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