dom/system/gonk/AutoMounter.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial