dom/system/gonk/Volume.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/system/gonk/Volume.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,408 @@
     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 "Volume.h"
     1.9 +#include "VolumeCommand.h"
    1.10 +#include "VolumeManager.h"
    1.11 +#include "VolumeManagerLog.h"
    1.12 +#include "nsIVolume.h"
    1.13 +#include "nsXULAppAPI.h"
    1.14 +
    1.15 +#include <vold/ResponseCode.h>
    1.16 +
    1.17 +namespace mozilla {
    1.18 +namespace system {
    1.19 +
    1.20 +Volume::EventObserverList Volume::mEventObserverList;
    1.21 +
    1.22 +// We have a feature where volumes can be locked when mounted. This
    1.23 +// is used to prevent a volume from being shared with the PC while
    1.24 +// it is actively being used (say for storing an update image)
    1.25 +//
    1.26 +// We use WakeLocks (a poor choice of name, but it does what we want)
    1.27 +// from the PowerManagerService to determine when we're locked.
    1.28 +// In particular we'll create a wakelock called volume-NAME-GENERATION
    1.29 +// (where NAME is the volume name, and GENERATION is its generation
    1.30 +// number), and if this wakelock is locked, then we'll prevent a volume
    1.31 +// from being shared.
    1.32 +//
    1.33 +// Implementation Details:
    1.34 +//
    1.35 +// Since the AutoMounter can only control when something gets mounted
    1.36 +// and not when it gets unmounted (for example: a user pulls the SDCard)
    1.37 +// and because Volume and nsVolume data structures are maintained on
    1.38 +// separate threads, we have the potential for some race conditions.
    1.39 +// We eliminate the race conditions by introducing the concept of a
    1.40 +// generation number. Every time a volume transitions to the Mounted
    1.41 +// state, it gets assigned a new generation number. Whenever the state
    1.42 +// of a Volume changes, we send the updated state and current generation
    1.43 +// number to the main thread where it gets updated in the nsVolume.
    1.44 +//
    1.45 +// Since WakeLocks can only be queried from the main-thread, the
    1.46 +// nsVolumeService looks for WakeLock status changes, and forwards
    1.47 +// the results to the IOThread.
    1.48 +//
    1.49 +// If the Volume (IOThread) recieves a volume update where the generation
    1.50 +// number mismatches, then the update is simply ignored.
    1.51 +//
    1.52 +// When a Volume (IOThread) initially becomes mounted, we assume it to
    1.53 +// be locked until we get our first update from nsVolume (MainThread).
    1.54 +static int32_t sMountGeneration = 0;
    1.55 +
    1.56 +// We don't get media inserted/removed events at startup. So we
    1.57 +// assume it's present, and we'll be told that it's missing.
    1.58 +Volume::Volume(const nsCSubstring& aName)
    1.59 +  : mMediaPresent(true),
    1.60 +    mState(nsIVolume::STATE_INIT),
    1.61 +    mName(aName),
    1.62 +    mMountGeneration(-1),
    1.63 +    mMountLocked(true),  // Needs to agree with nsVolume::nsVolume
    1.64 +    mSharingEnabled(false),
    1.65 +    mCanBeShared(true),
    1.66 +    mIsSharing(false),
    1.67 +    mFormatRequested(false),
    1.68 +    mMountRequested(false),
    1.69 +    mUnmountRequested(false),
    1.70 +    mIsFormatting(false)
    1.71 +{
    1.72 +  DBG("Volume %s: created", NameStr());
    1.73 +}
    1.74 +
    1.75 +void
    1.76 +Volume::SetIsSharing(bool aIsSharing)
    1.77 +{
    1.78 +  if (aIsSharing == mIsSharing) {
    1.79 +    return;
    1.80 +  }
    1.81 +  mIsSharing = aIsSharing;
    1.82 +  LOG("Volume %s: IsSharing set to %d state %s",
    1.83 +      NameStr(), (int)mIsSharing, StateStr(mState));
    1.84 +  if (mIsSharing) {
    1.85 +    mEventObserverList.Broadcast(this);
    1.86 +  }
    1.87 +}
    1.88 +
    1.89 +void
    1.90 +Volume::SetIsFormatting(bool aIsFormatting)
    1.91 +{
    1.92 +  if (aIsFormatting == mIsFormatting) {
    1.93 +    return;
    1.94 +  }
    1.95 +  mIsFormatting = aIsFormatting;
    1.96 +  LOG("Volume %s: IsFormatting set to %d state %s",
    1.97 +      NameStr(), (int)mIsFormatting, StateStr(mState));
    1.98 +  if (mIsFormatting) {
    1.99 +    mEventObserverList.Broadcast(this);
   1.100 +  }
   1.101 +}
   1.102 +
   1.103 +void
   1.104 +Volume::SetMediaPresent(bool aMediaPresent)
   1.105 +{
   1.106 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.107 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.108 +
   1.109 +  // mMediaPresent is slightly redunant to the state, however
   1.110 +  // when media is removed (while Idle), we get the following:
   1.111 +  //    631 Volume sdcard /mnt/sdcard disk removed (179:0)
   1.112 +  //    605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
   1.113 +  //
   1.114 +  // And on media insertion, we get:
   1.115 +  //    630 Volume sdcard /mnt/sdcard disk inserted (179:0)
   1.116 +  //    605 Volume sdcard /mnt/sdcard state changed from 0 (No-Media) to 2 (Pending)
   1.117 +  //    605 Volume sdcard /mnt/sdcard state changed from 2 (Pending) to 1 (Idle-Unmounted)
   1.118 +  //
   1.119 +  // On media removal while the media is mounted:
   1.120 +  //    632 Volume sdcard /mnt/sdcard bad removal (179:1)
   1.121 +  //    605 Volume sdcard /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)
   1.122 +  //    605 Volume sdcard /mnt/sdcard state changed from 5 (Unmounting) to 1 (Idle-Unmounted)
   1.123 +  //    631 Volume sdcard /mnt/sdcard disk removed (179:0)
   1.124 +  //    605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
   1.125 +  //
   1.126 +  // When sharing with a PC, it goes Mounted -> Idle -> Shared
   1.127 +  // When unsharing with a PC, it goes Shared -> Idle -> Mounted
   1.128 +  //
   1.129 +  // The AutoMounter needs to know whether the media is present or not when
   1.130 +  // processing the Idle state.
   1.131 +
   1.132 +  if (mMediaPresent == aMediaPresent) {
   1.133 +    return;
   1.134 +  }
   1.135 +
   1.136 +  LOG("Volume: %s media %s", NameStr(), aMediaPresent ? "inserted" : "removed");
   1.137 +  mMediaPresent = aMediaPresent;
   1.138 +  mEventObserverList.Broadcast(this);
   1.139 +}
   1.140 +
   1.141 +void
   1.142 +Volume::SetSharingEnabled(bool aSharingEnabled)
   1.143 +{
   1.144 +  mSharingEnabled = aSharingEnabled;
   1.145 +
   1.146 +  LOG("SetSharingMode for volume %s to %d canBeShared = %d",
   1.147 +      NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
   1.148 +}
   1.149 +
   1.150 +void
   1.151 +Volume::SetFormatRequested(bool aFormatRequested)
   1.152 +{
   1.153 +  mFormatRequested = aFormatRequested;
   1.154 +
   1.155 +  LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d",
   1.156 +      NameStr(), (int)mFormatRequested, (int)CanBeFormatted());
   1.157 +}
   1.158 +
   1.159 +void
   1.160 +Volume::SetMountRequested(bool aMountRequested)
   1.161 +{
   1.162 +  mMountRequested = aMountRequested;
   1.163 +
   1.164 +  LOG("SetMountRequested for volume %s to %d CanBeMounted = %d",
   1.165 +      NameStr(), (int)mMountRequested, (int)CanBeMounted());
   1.166 +}
   1.167 +
   1.168 +void
   1.169 +Volume::SetUnmountRequested(bool aUnmountRequested)
   1.170 +{
   1.171 +  mUnmountRequested = aUnmountRequested;
   1.172 +
   1.173 +  LOG("SetUnmountRequested for volume %s to %d CanBeMounted = %d",
   1.174 +      NameStr(), (int)mUnmountRequested, (int)CanBeMounted());
   1.175 +}
   1.176 +
   1.177 +void
   1.178 +Volume::SetState(Volume::STATE aNewState)
   1.179 +{
   1.180 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.181 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.182 +  if (aNewState == mState) {
   1.183 +    return;
   1.184 +  }
   1.185 +  if (aNewState == nsIVolume::STATE_MOUNTED) {
   1.186 +    mMountGeneration = ++sMountGeneration;
   1.187 +    LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) "
   1.188 +        "mountGeneration = %d, locked = %d",
   1.189 +        NameStr(), StateStr(mState),
   1.190 +        StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length(),
   1.191 +        mMountGeneration, (int)mMountLocked);
   1.192 +  } else {
   1.193 +    LOG("Volume %s: changing state from %s to %s (%d observers)",
   1.194 +        NameStr(), StateStr(mState),
   1.195 +        StateStr(aNewState), mEventObserverList.Length());
   1.196 +  }
   1.197 +
   1.198 +  switch (aNewState) {
   1.199 +     case nsIVolume::STATE_NOMEDIA:
   1.200 +       // Cover the startup case where we don't get insertion/removal events
   1.201 +       mMediaPresent = false;
   1.202 +       mIsSharing = false;
   1.203 +       mUnmountRequested = false;
   1.204 +       mMountRequested = false;
   1.205 +       break;
   1.206 +
   1.207 +     case nsIVolume::STATE_MOUNTED:
   1.208 +       mMountRequested = false;
   1.209 +       mIsFormatting = false;
   1.210 +       mIsSharing = false;
   1.211 +       break;
   1.212 +     case nsIVolume::STATE_FORMATTING:
   1.213 +       mFormatRequested = false;
   1.214 +       mIsFormatting = true;
   1.215 +       mIsSharing = false;
   1.216 +       break;
   1.217 +
   1.218 +     case nsIVolume::STATE_SHARED:
   1.219 +     case nsIVolume::STATE_SHAREDMNT:
   1.220 +       // Covers startup cases. Normally, mIsSharing would be set to true
   1.221 +       // when we issue the command to initiate the sharing process, but
   1.222 +       // it's conceivable that a volume could already be in a shared state
   1.223 +       // when b2g starts.
   1.224 +       mIsSharing = true;
   1.225 +       break;
   1.226 +
   1.227 +     case nsIVolume::STATE_IDLE:
   1.228 +       break;
   1.229 +     default:
   1.230 +       break;
   1.231 +  }
   1.232 +  mState = aNewState;
   1.233 +  mEventObserverList.Broadcast(this);
   1.234 +}
   1.235 +
   1.236 +void
   1.237 +Volume::SetMountPoint(const nsCSubstring& aMountPoint)
   1.238 +{
   1.239 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.240 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.241 +
   1.242 +  if (mMountPoint.Equals(aMountPoint)) {
   1.243 +    return;
   1.244 +  }
   1.245 +  mMountPoint = aMountPoint;
   1.246 +  DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
   1.247 +}
   1.248 +
   1.249 +void
   1.250 +Volume::StartMount(VolumeResponseCallback* aCallback)
   1.251 +{
   1.252 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.253 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.254 +
   1.255 +  StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
   1.256 +}
   1.257 +
   1.258 +void
   1.259 +Volume::StartUnmount(VolumeResponseCallback* aCallback)
   1.260 +{
   1.261 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.262 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.263 +
   1.264 +  StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
   1.265 +}
   1.266 +
   1.267 +void
   1.268 +Volume::StartFormat(VolumeResponseCallback* aCallback)
   1.269 +{
   1.270 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.271 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.272 +
   1.273 +  StartCommand(new VolumeActionCommand(this, "format", "", aCallback));
   1.274 +}
   1.275 +
   1.276 +void
   1.277 +Volume::StartShare(VolumeResponseCallback* aCallback)
   1.278 +{
   1.279 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.280 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.281 +
   1.282 +  StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
   1.283 +}
   1.284 +
   1.285 +void
   1.286 +Volume::StartUnshare(VolumeResponseCallback* aCallback)
   1.287 +{
   1.288 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.289 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.290 +
   1.291 +  StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
   1.292 +}
   1.293 +
   1.294 +void
   1.295 +Volume::StartCommand(VolumeCommand* aCommand)
   1.296 +{
   1.297 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.298 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.299 +
   1.300 +  VolumeManager::PostCommand(aCommand);
   1.301 +}
   1.302 +
   1.303 +//static
   1.304 +void
   1.305 +Volume::RegisterObserver(Volume::EventObserver* aObserver)
   1.306 +{
   1.307 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.308 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.309 +
   1.310 +  mEventObserverList.AddObserver(aObserver);
   1.311 +  // Send an initial event to the observer (for each volume)
   1.312 +  size_t numVolumes = VolumeManager::NumVolumes();
   1.313 +  for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.314 +    RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
   1.315 +    aObserver->Notify(vol);
   1.316 +  }
   1.317 +}
   1.318 +
   1.319 +//static
   1.320 +void
   1.321 +Volume::UnregisterObserver(Volume::EventObserver* aObserver)
   1.322 +{
   1.323 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.324 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.325 +
   1.326 +  mEventObserverList.RemoveObserver(aObserver);
   1.327 +}
   1.328 +
   1.329 +//static
   1.330 +void
   1.331 +Volume::UpdateMountLock(const nsACString& aVolumeName,
   1.332 +                        const int32_t& aMountGeneration,
   1.333 +                        const bool& aMountLocked)
   1.334 +{
   1.335 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.336 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.337 +
   1.338 +  RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
   1.339 +  if (!vol || (vol->mMountGeneration != aMountGeneration)) {
   1.340 +    return;
   1.341 +  }
   1.342 +  if (vol->mMountLocked != aMountLocked) {
   1.343 +    vol->mMountLocked = aMountLocked;
   1.344 +    DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
   1.345 +    mEventObserverList.Broadcast(vol);
   1.346 +  }
   1.347 +}
   1.348 +
   1.349 +void
   1.350 +Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer)
   1.351 +{
   1.352 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.353 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.354 +
   1.355 +  // The volume name will have already been parsed, and the tokenizer will point
   1.356 +  // to the token after the volume name
   1.357 +  switch (aResponseCode) {
   1.358 +    case ResponseCode::VolumeListResult: {
   1.359 +      // Each line will look something like:
   1.360 +      //
   1.361 +      //  sdcard /mnt/sdcard 1
   1.362 +      //
   1.363 +      nsDependentCSubstring mntPoint(aTokenizer.nextToken());
   1.364 +      SetMountPoint(mntPoint);
   1.365 +      nsresult errCode;
   1.366 +      nsCString state(aTokenizer.nextToken());
   1.367 +      if (state.EqualsLiteral("X")) {
   1.368 +        // Special state for creating fake volumes which can't be shared.
   1.369 +        mCanBeShared = false;
   1.370 +        SetState(nsIVolume::STATE_MOUNTED);
   1.371 +      } else {
   1.372 +        SetState((STATE)state.ToInteger(&errCode));
   1.373 +      }
   1.374 +      break;
   1.375 +    }
   1.376 +
   1.377 +    case ResponseCode::VolumeStateChange: {
   1.378 +      // Format of the line looks something like:
   1.379 +      //
   1.380 +      //  Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
   1.381 +      //
   1.382 +      // So we parse out the state after the string " to "
   1.383 +      while (aTokenizer.hasMoreTokens()) {
   1.384 +        nsAutoCString token(aTokenizer.nextToken());
   1.385 +        if (token.Equals("to")) {
   1.386 +          nsresult errCode;
   1.387 +          token = aTokenizer.nextToken();
   1.388 +          SetState((STATE)token.ToInteger(&errCode));
   1.389 +          break;
   1.390 +        }
   1.391 +      }
   1.392 +      break;
   1.393 +    }
   1.394 +
   1.395 +    case ResponseCode::VolumeDiskInserted:
   1.396 +      SetMediaPresent(true);
   1.397 +      break;
   1.398 +
   1.399 +    case ResponseCode::VolumeDiskRemoved: // fall-thru
   1.400 +    case ResponseCode::VolumeBadRemoval:
   1.401 +      SetMediaPresent(false);
   1.402 +      break;
   1.403 +
   1.404 +    default:
   1.405 +      LOG("Volume: %s unrecognized reponse code (ignored)", NameStr());
   1.406 +      break;
   1.407 +  }
   1.408 +}
   1.409 +
   1.410 +} // namespace system
   1.411 +} // namespace mozilla

mercurial