dom/system/gonk/Volume.cpp

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

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

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

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "Volume.h"
     6 #include "VolumeCommand.h"
     7 #include "VolumeManager.h"
     8 #include "VolumeManagerLog.h"
     9 #include "nsIVolume.h"
    10 #include "nsXULAppAPI.h"
    12 #include <vold/ResponseCode.h>
    14 namespace mozilla {
    15 namespace system {
    17 Volume::EventObserverList Volume::mEventObserverList;
    19 // We have a feature where volumes can be locked when mounted. This
    20 // is used to prevent a volume from being shared with the PC while
    21 // it is actively being used (say for storing an update image)
    22 //
    23 // We use WakeLocks (a poor choice of name, but it does what we want)
    24 // from the PowerManagerService to determine when we're locked.
    25 // In particular we'll create a wakelock called volume-NAME-GENERATION
    26 // (where NAME is the volume name, and GENERATION is its generation
    27 // number), and if this wakelock is locked, then we'll prevent a volume
    28 // from being shared.
    29 //
    30 // Implementation Details:
    31 //
    32 // Since the AutoMounter can only control when something gets mounted
    33 // and not when it gets unmounted (for example: a user pulls the SDCard)
    34 // and because Volume and nsVolume data structures are maintained on
    35 // separate threads, we have the potential for some race conditions.
    36 // We eliminate the race conditions by introducing the concept of a
    37 // generation number. Every time a volume transitions to the Mounted
    38 // state, it gets assigned a new generation number. Whenever the state
    39 // of a Volume changes, we send the updated state and current generation
    40 // number to the main thread where it gets updated in the nsVolume.
    41 //
    42 // Since WakeLocks can only be queried from the main-thread, the
    43 // nsVolumeService looks for WakeLock status changes, and forwards
    44 // the results to the IOThread.
    45 //
    46 // If the Volume (IOThread) recieves a volume update where the generation
    47 // number mismatches, then the update is simply ignored.
    48 //
    49 // When a Volume (IOThread) initially becomes mounted, we assume it to
    50 // be locked until we get our first update from nsVolume (MainThread).
    51 static int32_t sMountGeneration = 0;
    53 // We don't get media inserted/removed events at startup. So we
    54 // assume it's present, and we'll be told that it's missing.
    55 Volume::Volume(const nsCSubstring& aName)
    56   : mMediaPresent(true),
    57     mState(nsIVolume::STATE_INIT),
    58     mName(aName),
    59     mMountGeneration(-1),
    60     mMountLocked(true),  // Needs to agree with nsVolume::nsVolume
    61     mSharingEnabled(false),
    62     mCanBeShared(true),
    63     mIsSharing(false),
    64     mFormatRequested(false),
    65     mMountRequested(false),
    66     mUnmountRequested(false),
    67     mIsFormatting(false)
    68 {
    69   DBG("Volume %s: created", NameStr());
    70 }
    72 void
    73 Volume::SetIsSharing(bool aIsSharing)
    74 {
    75   if (aIsSharing == mIsSharing) {
    76     return;
    77   }
    78   mIsSharing = aIsSharing;
    79   LOG("Volume %s: IsSharing set to %d state %s",
    80       NameStr(), (int)mIsSharing, StateStr(mState));
    81   if (mIsSharing) {
    82     mEventObserverList.Broadcast(this);
    83   }
    84 }
    86 void
    87 Volume::SetIsFormatting(bool aIsFormatting)
    88 {
    89   if (aIsFormatting == mIsFormatting) {
    90     return;
    91   }
    92   mIsFormatting = aIsFormatting;
    93   LOG("Volume %s: IsFormatting set to %d state %s",
    94       NameStr(), (int)mIsFormatting, StateStr(mState));
    95   if (mIsFormatting) {
    96     mEventObserverList.Broadcast(this);
    97   }
    98 }
   100 void
   101 Volume::SetMediaPresent(bool aMediaPresent)
   102 {
   103   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   104   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   106   // mMediaPresent is slightly redunant to the state, however
   107   // when media is removed (while Idle), we get the following:
   108   //    631 Volume sdcard /mnt/sdcard disk removed (179:0)
   109   //    605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
   110   //
   111   // And on media insertion, we get:
   112   //    630 Volume sdcard /mnt/sdcard disk inserted (179:0)
   113   //    605 Volume sdcard /mnt/sdcard state changed from 0 (No-Media) to 2 (Pending)
   114   //    605 Volume sdcard /mnt/sdcard state changed from 2 (Pending) to 1 (Idle-Unmounted)
   115   //
   116   // On media removal while the media is mounted:
   117   //    632 Volume sdcard /mnt/sdcard bad removal (179:1)
   118   //    605 Volume sdcard /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)
   119   //    605 Volume sdcard /mnt/sdcard state changed from 5 (Unmounting) to 1 (Idle-Unmounted)
   120   //    631 Volume sdcard /mnt/sdcard disk removed (179:0)
   121   //    605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
   122   //
   123   // When sharing with a PC, it goes Mounted -> Idle -> Shared
   124   // When unsharing with a PC, it goes Shared -> Idle -> Mounted
   125   //
   126   // The AutoMounter needs to know whether the media is present or not when
   127   // processing the Idle state.
   129   if (mMediaPresent == aMediaPresent) {
   130     return;
   131   }
   133   LOG("Volume: %s media %s", NameStr(), aMediaPresent ? "inserted" : "removed");
   134   mMediaPresent = aMediaPresent;
   135   mEventObserverList.Broadcast(this);
   136 }
   138 void
   139 Volume::SetSharingEnabled(bool aSharingEnabled)
   140 {
   141   mSharingEnabled = aSharingEnabled;
   143   LOG("SetSharingMode for volume %s to %d canBeShared = %d",
   144       NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
   145 }
   147 void
   148 Volume::SetFormatRequested(bool aFormatRequested)
   149 {
   150   mFormatRequested = aFormatRequested;
   152   LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d",
   153       NameStr(), (int)mFormatRequested, (int)CanBeFormatted());
   154 }
   156 void
   157 Volume::SetMountRequested(bool aMountRequested)
   158 {
   159   mMountRequested = aMountRequested;
   161   LOG("SetMountRequested for volume %s to %d CanBeMounted = %d",
   162       NameStr(), (int)mMountRequested, (int)CanBeMounted());
   163 }
   165 void
   166 Volume::SetUnmountRequested(bool aUnmountRequested)
   167 {
   168   mUnmountRequested = aUnmountRequested;
   170   LOG("SetUnmountRequested for volume %s to %d CanBeMounted = %d",
   171       NameStr(), (int)mUnmountRequested, (int)CanBeMounted());
   172 }
   174 void
   175 Volume::SetState(Volume::STATE aNewState)
   176 {
   177   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   178   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   179   if (aNewState == mState) {
   180     return;
   181   }
   182   if (aNewState == nsIVolume::STATE_MOUNTED) {
   183     mMountGeneration = ++sMountGeneration;
   184     LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) "
   185         "mountGeneration = %d, locked = %d",
   186         NameStr(), StateStr(mState),
   187         StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length(),
   188         mMountGeneration, (int)mMountLocked);
   189   } else {
   190     LOG("Volume %s: changing state from %s to %s (%d observers)",
   191         NameStr(), StateStr(mState),
   192         StateStr(aNewState), mEventObserverList.Length());
   193   }
   195   switch (aNewState) {
   196      case nsIVolume::STATE_NOMEDIA:
   197        // Cover the startup case where we don't get insertion/removal events
   198        mMediaPresent = false;
   199        mIsSharing = false;
   200        mUnmountRequested = false;
   201        mMountRequested = false;
   202        break;
   204      case nsIVolume::STATE_MOUNTED:
   205        mMountRequested = false;
   206        mIsFormatting = false;
   207        mIsSharing = false;
   208        break;
   209      case nsIVolume::STATE_FORMATTING:
   210        mFormatRequested = false;
   211        mIsFormatting = true;
   212        mIsSharing = false;
   213        break;
   215      case nsIVolume::STATE_SHARED:
   216      case nsIVolume::STATE_SHAREDMNT:
   217        // Covers startup cases. Normally, mIsSharing would be set to true
   218        // when we issue the command to initiate the sharing process, but
   219        // it's conceivable that a volume could already be in a shared state
   220        // when b2g starts.
   221        mIsSharing = true;
   222        break;
   224      case nsIVolume::STATE_IDLE:
   225        break;
   226      default:
   227        break;
   228   }
   229   mState = aNewState;
   230   mEventObserverList.Broadcast(this);
   231 }
   233 void
   234 Volume::SetMountPoint(const nsCSubstring& aMountPoint)
   235 {
   236   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   237   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   239   if (mMountPoint.Equals(aMountPoint)) {
   240     return;
   241   }
   242   mMountPoint = aMountPoint;
   243   DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
   244 }
   246 void
   247 Volume::StartMount(VolumeResponseCallback* aCallback)
   248 {
   249   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   250   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   252   StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
   253 }
   255 void
   256 Volume::StartUnmount(VolumeResponseCallback* aCallback)
   257 {
   258   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   259   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   261   StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
   262 }
   264 void
   265 Volume::StartFormat(VolumeResponseCallback* aCallback)
   266 {
   267   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   268   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   270   StartCommand(new VolumeActionCommand(this, "format", "", aCallback));
   271 }
   273 void
   274 Volume::StartShare(VolumeResponseCallback* aCallback)
   275 {
   276   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   277   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   279   StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
   280 }
   282 void
   283 Volume::StartUnshare(VolumeResponseCallback* aCallback)
   284 {
   285   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   286   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   288   StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
   289 }
   291 void
   292 Volume::StartCommand(VolumeCommand* aCommand)
   293 {
   294   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   295   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   297   VolumeManager::PostCommand(aCommand);
   298 }
   300 //static
   301 void
   302 Volume::RegisterObserver(Volume::EventObserver* aObserver)
   303 {
   304   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   305   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   307   mEventObserverList.AddObserver(aObserver);
   308   // Send an initial event to the observer (for each volume)
   309   size_t numVolumes = VolumeManager::NumVolumes();
   310   for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
   311     RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
   312     aObserver->Notify(vol);
   313   }
   314 }
   316 //static
   317 void
   318 Volume::UnregisterObserver(Volume::EventObserver* aObserver)
   319 {
   320   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   321   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   323   mEventObserverList.RemoveObserver(aObserver);
   324 }
   326 //static
   327 void
   328 Volume::UpdateMountLock(const nsACString& aVolumeName,
   329                         const int32_t& aMountGeneration,
   330                         const bool& aMountLocked)
   331 {
   332   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   333   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   335   RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
   336   if (!vol || (vol->mMountGeneration != aMountGeneration)) {
   337     return;
   338   }
   339   if (vol->mMountLocked != aMountLocked) {
   340     vol->mMountLocked = aMountLocked;
   341     DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
   342     mEventObserverList.Broadcast(vol);
   343   }
   344 }
   346 void
   347 Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer)
   348 {
   349   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   350   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   352   // The volume name will have already been parsed, and the tokenizer will point
   353   // to the token after the volume name
   354   switch (aResponseCode) {
   355     case ResponseCode::VolumeListResult: {
   356       // Each line will look something like:
   357       //
   358       //  sdcard /mnt/sdcard 1
   359       //
   360       nsDependentCSubstring mntPoint(aTokenizer.nextToken());
   361       SetMountPoint(mntPoint);
   362       nsresult errCode;
   363       nsCString state(aTokenizer.nextToken());
   364       if (state.EqualsLiteral("X")) {
   365         // Special state for creating fake volumes which can't be shared.
   366         mCanBeShared = false;
   367         SetState(nsIVolume::STATE_MOUNTED);
   368       } else {
   369         SetState((STATE)state.ToInteger(&errCode));
   370       }
   371       break;
   372     }
   374     case ResponseCode::VolumeStateChange: {
   375       // Format of the line looks something like:
   376       //
   377       //  Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
   378       //
   379       // So we parse out the state after the string " to "
   380       while (aTokenizer.hasMoreTokens()) {
   381         nsAutoCString token(aTokenizer.nextToken());
   382         if (token.Equals("to")) {
   383           nsresult errCode;
   384           token = aTokenizer.nextToken();
   385           SetState((STATE)token.ToInteger(&errCode));
   386           break;
   387         }
   388       }
   389       break;
   390     }
   392     case ResponseCode::VolumeDiskInserted:
   393       SetMediaPresent(true);
   394       break;
   396     case ResponseCode::VolumeDiskRemoved: // fall-thru
   397     case ResponseCode::VolumeBadRemoval:
   398       SetMediaPresent(false);
   399       break;
   401     default:
   402       LOG("Volume: %s unrecognized reponse code (ignored)", NameStr());
   403       break;
   404   }
   405 }
   407 } // namespace system
   408 } // namespace mozilla

mercurial