dom/system/gonk/nsVolumeService.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 "nsVolumeService.h"
     7 #include "Volume.h"
     8 #include "VolumeManager.h"
     9 #include "VolumeServiceIOThread.h"
    11 #include "nsAutoPtr.h"
    12 #include "nsCOMPtr.h"
    13 #include "nsDependentSubstring.h"
    14 #include "nsIDOMWakeLockListener.h"
    15 #include "nsIObserver.h"
    16 #include "nsIObserverService.h"
    17 #include "nsIPowerManagerService.h"
    18 #include "nsISupportsUtils.h"
    19 #include "nsIVolume.h"
    20 #include "nsIVolumeService.h"
    21 #include "nsLocalFile.h"
    22 #include "nsServiceManagerUtils.h"
    23 #include "nsString.h"
    24 #include "nsTArray.h"
    25 #include "nsThreadUtils.h"
    26 #include "nsVolumeMountLock.h"
    27 #include "nsXULAppAPI.h"
    28 #include "mozilla/dom/ContentChild.h"
    29 #include "mozilla/Services.h"
    31 #define VOLUME_MANAGER_LOG_TAG  "nsVolumeService"
    32 #include "VolumeManagerLog.h"
    34 #include <stdlib.h>
    36 using namespace mozilla::dom;
    37 using namespace mozilla::services;
    39 namespace mozilla {
    40 namespace system {
    42 NS_IMPL_ISUPPORTS(nsVolumeService,
    43                   nsIVolumeService,
    44                   nsIDOMMozWakeLockListener)
    46 StaticRefPtr<nsVolumeService> nsVolumeService::sSingleton;
    48 // static
    49 already_AddRefed<nsVolumeService>
    50 nsVolumeService::GetSingleton()
    51 {
    52   MOZ_ASSERT(NS_IsMainThread());
    54   if (!sSingleton) {
    55     sSingleton = new nsVolumeService();
    56   }
    57   nsRefPtr<nsVolumeService> volumeService = sSingleton.get();
    58   return volumeService.forget();
    59 }
    61 // static
    62 void
    63 nsVolumeService::Shutdown()
    64 {
    65   if (!sSingleton) {
    66     return;
    67   }
    68   if (XRE_GetProcessType() != GeckoProcessType_Default) {
    69     sSingleton = nullptr;
    70     return;
    71   }
    73   nsCOMPtr<nsIPowerManagerService> pmService =
    74     do_GetService(POWERMANAGERSERVICE_CONTRACTID);
    75   if (pmService) {
    76     pmService->RemoveWakeLockListener(sSingleton.get());
    77   }
    79   XRE_GetIOMessageLoop()->PostTask(
    80       FROM_HERE,
    81       NewRunnableFunction(ShutdownVolumeServiceIOThread));
    83   sSingleton = nullptr;
    84 }
    86 nsVolumeService::nsVolumeService()
    87   : mArrayMonitor("nsVolumeServiceArray")
    88 {
    89   sSingleton = this;
    91   if (XRE_GetProcessType() != GeckoProcessType_Default) {
    92     // Request the initial state for all volumes.
    93     ContentChild::GetSingleton()->SendBroadcastVolume(NS_LITERAL_STRING(""));
    94     return;
    95   }
    97   // Startup the IOThread side of things. The actual volume changes
    98   // are captured by the IOThread and forwarded to main thread.
    99   XRE_GetIOMessageLoop()->PostTask(
   100       FROM_HERE,
   101       NewRunnableFunction(InitVolumeServiceIOThread, this));
   103   nsCOMPtr<nsIPowerManagerService> pmService =
   104     do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   105   if (!pmService) {
   106     return;
   107   }
   108   pmService->AddWakeLockListener(this);
   109 }
   111 nsVolumeService::~nsVolumeService()
   112 {
   113 }
   115 // Callback for nsIDOMMozWakeLockListener
   116 NS_IMETHODIMP
   117 nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
   118 {
   119   CheckMountLock(aTopic, aState);
   120   return NS_OK;
   121 }
   123 NS_IMETHODIMP
   124 nsVolumeService::BroadcastVolume(const nsAString& aVolName)
   125 {
   126   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   128   if (aVolName.EqualsLiteral("")) {
   129     nsVolume::Array volumeArray;
   130     {
   131       // Copy the array since we don't want to call BroadcastVolume
   132       // while we're holding the lock.
   133       MonitorAutoLock autoLock(mArrayMonitor);
   134       volumeArray = mVolumeArray;
   135     }
   137     // We treat being passed the empty string as "broadcast all volumes"
   138     nsVolume::Array::size_type numVolumes = volumeArray.Length();
   139     nsVolume::Array::index_type volIndex;
   140     for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   141       const nsString& volName(volumeArray[volIndex]->Name());
   142       if (!volName.EqualsLiteral("")) {
   143         // Note: The volume service is the only entity that should be able to
   144         // modify the array of volumes. So we shouldn't have any issues with
   145         // the array being modified under our feet (Since we're the volume
   146         // service the array can't change until after we finish iterating the
   147         // the loop).
   148         nsresult rv = BroadcastVolume(volName);
   149         NS_ENSURE_SUCCESS(rv, rv);
   150       }
   151     }
   152     return NS_OK;
   153   }
   154   nsRefPtr<nsVolume> vol;
   155   {
   156     MonitorAutoLock autoLock(mArrayMonitor);
   157     vol = FindVolumeByName(aVolName);
   158   }
   159   if (!vol) {
   160     ERR("BroadcastVolume: Unable to locate volume '%s'",
   161         NS_LossyConvertUTF16toASCII(aVolName).get());
   162     return NS_ERROR_NOT_AVAILABLE;
   163   }
   165   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   166   NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
   168   DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr().get());
   169   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   170   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   171   return NS_OK;
   172 }
   174 NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString& aVolName, nsIVolume **aResult)
   175 {
   176   MonitorAutoLock autoLock(mArrayMonitor);
   178   nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
   179   if (!vol) {
   180     return NS_ERROR_NOT_AVAILABLE;
   181   }
   183   vol.forget(aResult);
   184   return NS_OK;
   185 }
   187 NS_IMETHODIMP
   188 nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
   189 {
   190   NS_ConvertUTF16toUTF8 utf8Path(aPath);
   191   char realPathBuf[PATH_MAX];
   193   while (realpath(utf8Path.get(), realPathBuf) < 0) {
   194     if (errno != ENOENT) {
   195       ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
   196       return NSRESULT_FOR_ERRNO();
   197     }
   198     // The pathname we were passed doesn't exist, so we try stripping off trailing
   199     // components until we get a successful call to realpath, or until we run out
   200     // of components (if we finally get to /something then we also stop).
   201     int32_t slashIndex = utf8Path.RFindChar('/');
   202     if ((slashIndex == kNotFound) || (slashIndex == 0)) {
   203       errno = ENOENT;
   204       ERR("GetVolumeByPath: realpath on '%s' failed.", utf8Path.get());
   205       return NSRESULT_FOR_ERRNO();
   206     }
   207     utf8Path.Assign(Substring(utf8Path, 0, slashIndex));
   208   }
   210   // The volume mount point is always a directory. Something like /mnt/sdcard
   211   // Once we have a full qualified pathname with symlinks removed (which is
   212   // what realpath does), we basically check if aPath starts with the mount
   213   // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
   214   // do want it to match /mnt/sdcard/foo
   215   // So we add a trailing slash to the mount point and the pathname passed in
   216   // prior to doing the comparison.
   218   strlcat(realPathBuf, "/", sizeof(realPathBuf));
   220   MonitorAutoLock autoLock(mArrayMonitor);
   222   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   223   nsVolume::Array::index_type volIndex;
   224   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   225     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   226     NS_ConvertUTF16toUTF8 volMountPointSlash(vol->MountPoint());
   227     volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
   228     nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
   229     if (volMountPointSlash.Equals(testStr)) {
   230       vol.forget(aResult);
   231       return NS_OK;
   232     }
   233   }
   234   return NS_ERROR_FILE_NOT_FOUND;
   235 }
   237 NS_IMETHODIMP
   238 nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aResult)
   239 {
   240   nsresult rv = GetVolumeByPath(aPath, aResult);
   241   if (rv == NS_OK) {
   242     return NS_OK;
   243   }
   245   // In order to support queries by the updater, we will fabricate a volume
   246   // from the pathname, so that the caller can determine the volume size.
   247   nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
   248                                          aPath, nsIVolume::STATE_MOUNTED,
   249                                          -1    /* generation */,
   250                                          true  /* isMediaPresent*/,
   251                                          false /* isSharing */,
   252                                          false /* isFormatting */);
   253   vol.forget(aResult);
   254   return NS_OK;
   255 }
   257 NS_IMETHODIMP
   258 nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
   259 {
   260   MonitorAutoLock autoLock(mArrayMonitor);
   262   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   263   nsVolume::Array::index_type volIndex;
   264   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   265     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   266     aVolNames.AppendElement(vol->Name());
   267   }
   269   return NS_OK;
   270 }
   272 NS_IMETHODIMP
   273 nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
   274 {
   275   nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   276   if (!mountLock) {
   277     return NS_ERROR_NOT_AVAILABLE;
   278   }
   279   mountLock.forget(aResult);
   280   return NS_OK;
   281 }
   283 void
   284 nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
   285                                 const nsAString& aMountLockState)
   286 {
   287   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   288   MOZ_ASSERT(NS_IsMainThread());
   290   nsRefPtr<nsVolume> vol = FindVolumeByMountLockName(aMountLockName);
   291   if (vol) {
   292     vol->UpdateMountLock(aMountLockState);
   293   }
   294 }
   296 already_AddRefed<nsVolume>
   297 nsVolumeService::FindVolumeByMountLockName(const nsAString& aMountLockName)
   298 {
   299   MonitorAutoLock autoLock(mArrayMonitor);
   301   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   302   nsVolume::Array::index_type volIndex;
   303   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   304     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   305     nsString mountLockName;
   306     vol->GetMountLockName(mountLockName);
   307     if (mountLockName.Equals(aMountLockName)) {
   308       return vol.forget();
   309     }
   310   }
   311   return nullptr;
   312 }
   314 already_AddRefed<nsVolume>
   315 nsVolumeService::FindVolumeByName(const nsAString& aName)
   316 {
   317   mArrayMonitor.AssertCurrentThreadOwns();
   319   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   320   nsVolume::Array::index_type volIndex;
   321   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   322     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   323     if (vol->Name().Equals(aName)) {
   324       return vol.forget();
   325     }
   326   }
   327   return nullptr;
   328 }
   330 //static
   331 already_AddRefed<nsVolume>
   332 nsVolumeService::CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake /*= false*/)
   333 {
   334   MonitorAutoLock autoLock(mArrayMonitor);
   336   nsRefPtr<nsVolume> vol;
   337   vol = FindVolumeByName(aName);
   338   if (vol) {
   339     return vol.forget();
   340   }
   341   // Volume not found - add a new one
   342   vol = new nsVolume(aName);
   343   vol->SetIsFake(aIsFake);
   344   mVolumeArray.AppendElement(vol);
   345   return vol.forget();
   346 }
   348 void
   349 nsVolumeService::UpdateVolume(nsIVolume* aVolume)
   350 {
   351   MOZ_ASSERT(NS_IsMainThread());
   353   nsString volName;
   354   aVolume->GetName(volName);
   355   bool aIsFake;
   356   aVolume->GetIsFake(&aIsFake);
   357   nsRefPtr<nsVolume> vol = CreateOrFindVolumeByName(volName, aIsFake);
   358   if (vol->Equals(aVolume)) {
   359     // Nothing has really changed. Don't bother telling anybody.
   360     return;
   361   }
   363   if (!vol->IsFake() && aIsFake) {
   364     // Prevent an incoming fake volume from overriding an existing real volume.
   365     return;
   366   }
   368   vol->Set(aVolume);
   369   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   370   if (!obs) {
   371     return;
   372   }
   373   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   374   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   375 }
   377 NS_IMETHODIMP
   378 nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
   379 {
   380   if (XRE_GetProcessType() == GeckoProcessType_Default) {
   381     nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT,
   382                                           -1    /* mountGeneration */,
   383                                           true  /* isMediaPresent */,
   384                                           false /* isSharing */,
   385                                           false /* isFormatting */);
   386     vol->SetIsFake(true);
   387     vol->LogState();
   388     UpdateVolume(vol.get());
   389     return NS_OK;
   390   }
   392   ContentChild::GetSingleton()->SendCreateFakeVolume(nsString(name), nsString(path));
   393   return NS_OK;
   394 }
   396 NS_IMETHODIMP
   397 nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state)
   398 {
   399   if (XRE_GetProcessType() == GeckoProcessType_Default) {
   400     nsRefPtr<nsVolume> vol;
   401     {
   402       MonitorAutoLock autoLock(mArrayMonitor);
   403       vol = FindVolumeByName(name);
   404     }
   405     if (!vol || !vol->IsFake()) {
   406       return NS_ERROR_NOT_AVAILABLE;
   407     }
   408     vol->SetState(state);
   409     vol->LogState();
   410     UpdateVolume(vol.get());
   411     return NS_OK;
   412   }
   414   ContentChild::GetSingleton()->SendSetFakeVolumeState(nsString(name), state);
   415   return NS_OK;
   416 }
   418 /***************************************************************************
   419 * The UpdateVolumeRunnable creates an nsVolume and updates the main thread
   420 * data structure while running on the main thread.
   421 */
   422 class UpdateVolumeRunnable : public nsRunnable
   423 {
   424 public:
   425   UpdateVolumeRunnable(nsVolumeService* aVolumeService, const Volume* aVolume)
   426     : mVolumeService(aVolumeService),
   427       mVolume(new nsVolume(aVolume))
   428   {
   429     MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   430   }
   432   NS_IMETHOD Run()
   433   {
   434     MOZ_ASSERT(NS_IsMainThread());
   435     DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
   436         "media %d sharing %d formatting %d",
   437         mVolume->NameStr().get(), mVolume->StateStr(),
   438         mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
   439         (int)mVolume->IsMediaPresent(), mVolume->IsSharing(),
   440         mVolume->IsFormatting());
   442     mVolumeService->UpdateVolume(mVolume);
   443     mVolumeService = nullptr;
   444     mVolume = nullptr;
   445     return NS_OK;
   446   }
   448 private:
   449   nsRefPtr<nsVolumeService> mVolumeService;
   450   nsRefPtr<nsVolume>        mVolume;
   451 };
   453 void
   454 nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
   455 {
   456   DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
   457       "media %d sharing %d formatting %d",
   458       aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
   459       aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
   460       (int)aVolume->MediaPresent(), (int)aVolume->IsSharing(),
   461       (int)aVolume->IsFormatting());
   462   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   463   NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
   464 }
   466 } // namespace system
   467 } // namespace mozilla

mercurial