dom/system/gonk/nsVolumeService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/system/gonk/nsVolumeService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,467 @@
     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 "nsVolumeService.h"
     1.9 +
    1.10 +#include "Volume.h"
    1.11 +#include "VolumeManager.h"
    1.12 +#include "VolumeServiceIOThread.h"
    1.13 +
    1.14 +#include "nsAutoPtr.h"
    1.15 +#include "nsCOMPtr.h"
    1.16 +#include "nsDependentSubstring.h"
    1.17 +#include "nsIDOMWakeLockListener.h"
    1.18 +#include "nsIObserver.h"
    1.19 +#include "nsIObserverService.h"
    1.20 +#include "nsIPowerManagerService.h"
    1.21 +#include "nsISupportsUtils.h"
    1.22 +#include "nsIVolume.h"
    1.23 +#include "nsIVolumeService.h"
    1.24 +#include "nsLocalFile.h"
    1.25 +#include "nsServiceManagerUtils.h"
    1.26 +#include "nsString.h"
    1.27 +#include "nsTArray.h"
    1.28 +#include "nsThreadUtils.h"
    1.29 +#include "nsVolumeMountLock.h"
    1.30 +#include "nsXULAppAPI.h"
    1.31 +#include "mozilla/dom/ContentChild.h"
    1.32 +#include "mozilla/Services.h"
    1.33 +
    1.34 +#define VOLUME_MANAGER_LOG_TAG  "nsVolumeService"
    1.35 +#include "VolumeManagerLog.h"
    1.36 +
    1.37 +#include <stdlib.h>
    1.38 +
    1.39 +using namespace mozilla::dom;
    1.40 +using namespace mozilla::services;
    1.41 +
    1.42 +namespace mozilla {
    1.43 +namespace system {
    1.44 +
    1.45 +NS_IMPL_ISUPPORTS(nsVolumeService,
    1.46 +                  nsIVolumeService,
    1.47 +                  nsIDOMMozWakeLockListener)
    1.48 +
    1.49 +StaticRefPtr<nsVolumeService> nsVolumeService::sSingleton;
    1.50 +
    1.51 +// static
    1.52 +already_AddRefed<nsVolumeService>
    1.53 +nsVolumeService::GetSingleton()
    1.54 +{
    1.55 +  MOZ_ASSERT(NS_IsMainThread());
    1.56 +
    1.57 +  if (!sSingleton) {
    1.58 +    sSingleton = new nsVolumeService();
    1.59 +  }
    1.60 +  nsRefPtr<nsVolumeService> volumeService = sSingleton.get();
    1.61 +  return volumeService.forget();
    1.62 +}
    1.63 +
    1.64 +// static
    1.65 +void
    1.66 +nsVolumeService::Shutdown()
    1.67 +{
    1.68 +  if (!sSingleton) {
    1.69 +    return;
    1.70 +  }
    1.71 +  if (XRE_GetProcessType() != GeckoProcessType_Default) {
    1.72 +    sSingleton = nullptr;
    1.73 +    return;
    1.74 +  }
    1.75 +
    1.76 +  nsCOMPtr<nsIPowerManagerService> pmService =
    1.77 +    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
    1.78 +  if (pmService) {
    1.79 +    pmService->RemoveWakeLockListener(sSingleton.get());
    1.80 +  }
    1.81 +
    1.82 +  XRE_GetIOMessageLoop()->PostTask(
    1.83 +      FROM_HERE,
    1.84 +      NewRunnableFunction(ShutdownVolumeServiceIOThread));
    1.85 +
    1.86 +  sSingleton = nullptr;
    1.87 +}
    1.88 +
    1.89 +nsVolumeService::nsVolumeService()
    1.90 +  : mArrayMonitor("nsVolumeServiceArray")
    1.91 +{
    1.92 +  sSingleton = this;
    1.93 +
    1.94 +  if (XRE_GetProcessType() != GeckoProcessType_Default) {
    1.95 +    // Request the initial state for all volumes.
    1.96 +    ContentChild::GetSingleton()->SendBroadcastVolume(NS_LITERAL_STRING(""));
    1.97 +    return;
    1.98 +  }
    1.99 +
   1.100 +  // Startup the IOThread side of things. The actual volume changes
   1.101 +  // are captured by the IOThread and forwarded to main thread.
   1.102 +  XRE_GetIOMessageLoop()->PostTask(
   1.103 +      FROM_HERE,
   1.104 +      NewRunnableFunction(InitVolumeServiceIOThread, this));
   1.105 +
   1.106 +  nsCOMPtr<nsIPowerManagerService> pmService =
   1.107 +    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   1.108 +  if (!pmService) {
   1.109 +    return;
   1.110 +  }
   1.111 +  pmService->AddWakeLockListener(this);
   1.112 +}
   1.113 +
   1.114 +nsVolumeService::~nsVolumeService()
   1.115 +{
   1.116 +}
   1.117 +
   1.118 +// Callback for nsIDOMMozWakeLockListener
   1.119 +NS_IMETHODIMP
   1.120 +nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
   1.121 +{
   1.122 +  CheckMountLock(aTopic, aState);
   1.123 +  return NS_OK;
   1.124 +}
   1.125 +
   1.126 +NS_IMETHODIMP
   1.127 +nsVolumeService::BroadcastVolume(const nsAString& aVolName)
   1.128 +{
   1.129 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.130 +
   1.131 +  if (aVolName.EqualsLiteral("")) {
   1.132 +    nsVolume::Array volumeArray;
   1.133 +    {
   1.134 +      // Copy the array since we don't want to call BroadcastVolume
   1.135 +      // while we're holding the lock.
   1.136 +      MonitorAutoLock autoLock(mArrayMonitor);
   1.137 +      volumeArray = mVolumeArray;
   1.138 +    }
   1.139 +
   1.140 +    // We treat being passed the empty string as "broadcast all volumes"
   1.141 +    nsVolume::Array::size_type numVolumes = volumeArray.Length();
   1.142 +    nsVolume::Array::index_type volIndex;
   1.143 +    for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.144 +      const nsString& volName(volumeArray[volIndex]->Name());
   1.145 +      if (!volName.EqualsLiteral("")) {
   1.146 +        // Note: The volume service is the only entity that should be able to
   1.147 +        // modify the array of volumes. So we shouldn't have any issues with
   1.148 +        // the array being modified under our feet (Since we're the volume
   1.149 +        // service the array can't change until after we finish iterating the
   1.150 +        // the loop).
   1.151 +        nsresult rv = BroadcastVolume(volName);
   1.152 +        NS_ENSURE_SUCCESS(rv, rv);
   1.153 +      }
   1.154 +    }
   1.155 +    return NS_OK;
   1.156 +  }
   1.157 +  nsRefPtr<nsVolume> vol;
   1.158 +  {
   1.159 +    MonitorAutoLock autoLock(mArrayMonitor);
   1.160 +    vol = FindVolumeByName(aVolName);
   1.161 +  }
   1.162 +  if (!vol) {
   1.163 +    ERR("BroadcastVolume: Unable to locate volume '%s'",
   1.164 +        NS_LossyConvertUTF16toASCII(aVolName).get());
   1.165 +    return NS_ERROR_NOT_AVAILABLE;
   1.166 +  }
   1.167 +
   1.168 +  nsCOMPtr<nsIObserverService> obs = GetObserverService();
   1.169 +  NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
   1.170 +
   1.171 +  DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr().get());
   1.172 +  NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   1.173 +  obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   1.174 +  return NS_OK;
   1.175 +}
   1.176 +
   1.177 +NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString& aVolName, nsIVolume **aResult)
   1.178 +{
   1.179 +  MonitorAutoLock autoLock(mArrayMonitor);
   1.180 +
   1.181 +  nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
   1.182 +  if (!vol) {
   1.183 +    return NS_ERROR_NOT_AVAILABLE;
   1.184 +  }
   1.185 +
   1.186 +  vol.forget(aResult);
   1.187 +  return NS_OK;
   1.188 +}
   1.189 +
   1.190 +NS_IMETHODIMP
   1.191 +nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
   1.192 +{
   1.193 +  NS_ConvertUTF16toUTF8 utf8Path(aPath);
   1.194 +  char realPathBuf[PATH_MAX];
   1.195 +
   1.196 +  while (realpath(utf8Path.get(), realPathBuf) < 0) {
   1.197 +    if (errno != ENOENT) {
   1.198 +      ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
   1.199 +      return NSRESULT_FOR_ERRNO();
   1.200 +    }
   1.201 +    // The pathname we were passed doesn't exist, so we try stripping off trailing
   1.202 +    // components until we get a successful call to realpath, or until we run out
   1.203 +    // of components (if we finally get to /something then we also stop).
   1.204 +    int32_t slashIndex = utf8Path.RFindChar('/');
   1.205 +    if ((slashIndex == kNotFound) || (slashIndex == 0)) {
   1.206 +      errno = ENOENT;
   1.207 +      ERR("GetVolumeByPath: realpath on '%s' failed.", utf8Path.get());
   1.208 +      return NSRESULT_FOR_ERRNO();
   1.209 +    }
   1.210 +    utf8Path.Assign(Substring(utf8Path, 0, slashIndex));
   1.211 +  }
   1.212 +
   1.213 +  // The volume mount point is always a directory. Something like /mnt/sdcard
   1.214 +  // Once we have a full qualified pathname with symlinks removed (which is
   1.215 +  // what realpath does), we basically check if aPath starts with the mount
   1.216 +  // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
   1.217 +  // do want it to match /mnt/sdcard/foo
   1.218 +  // So we add a trailing slash to the mount point and the pathname passed in
   1.219 +  // prior to doing the comparison.
   1.220 +
   1.221 +  strlcat(realPathBuf, "/", sizeof(realPathBuf));
   1.222 +
   1.223 +  MonitorAutoLock autoLock(mArrayMonitor);
   1.224 +
   1.225 +  nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   1.226 +  nsVolume::Array::index_type volIndex;
   1.227 +  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.228 +    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   1.229 +    NS_ConvertUTF16toUTF8 volMountPointSlash(vol->MountPoint());
   1.230 +    volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
   1.231 +    nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
   1.232 +    if (volMountPointSlash.Equals(testStr)) {
   1.233 +      vol.forget(aResult);
   1.234 +      return NS_OK;
   1.235 +    }
   1.236 +  }
   1.237 +  return NS_ERROR_FILE_NOT_FOUND;
   1.238 +}
   1.239 +
   1.240 +NS_IMETHODIMP
   1.241 +nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aResult)
   1.242 +{
   1.243 +  nsresult rv = GetVolumeByPath(aPath, aResult);
   1.244 +  if (rv == NS_OK) {
   1.245 +    return NS_OK;
   1.246 +  }
   1.247 +
   1.248 +  // In order to support queries by the updater, we will fabricate a volume
   1.249 +  // from the pathname, so that the caller can determine the volume size.
   1.250 +  nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
   1.251 +                                         aPath, nsIVolume::STATE_MOUNTED,
   1.252 +                                         -1    /* generation */,
   1.253 +                                         true  /* isMediaPresent*/,
   1.254 +                                         false /* isSharing */,
   1.255 +                                         false /* isFormatting */);
   1.256 +  vol.forget(aResult);
   1.257 +  return NS_OK;
   1.258 +}
   1.259 +
   1.260 +NS_IMETHODIMP
   1.261 +nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
   1.262 +{
   1.263 +  MonitorAutoLock autoLock(mArrayMonitor);
   1.264 +
   1.265 +  nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   1.266 +  nsVolume::Array::index_type volIndex;
   1.267 +  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.268 +    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   1.269 +    aVolNames.AppendElement(vol->Name());
   1.270 +  }
   1.271 +
   1.272 +  return NS_OK;
   1.273 +}
   1.274 +
   1.275 +NS_IMETHODIMP
   1.276 +nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
   1.277 +{
   1.278 +  nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   1.279 +  if (!mountLock) {
   1.280 +    return NS_ERROR_NOT_AVAILABLE;
   1.281 +  }
   1.282 +  mountLock.forget(aResult);
   1.283 +  return NS_OK;
   1.284 +}
   1.285 +
   1.286 +void
   1.287 +nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
   1.288 +                                const nsAString& aMountLockState)
   1.289 +{
   1.290 +  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   1.291 +  MOZ_ASSERT(NS_IsMainThread());
   1.292 +
   1.293 +  nsRefPtr<nsVolume> vol = FindVolumeByMountLockName(aMountLockName);
   1.294 +  if (vol) {
   1.295 +    vol->UpdateMountLock(aMountLockState);
   1.296 +  }
   1.297 +}
   1.298 +
   1.299 +already_AddRefed<nsVolume>
   1.300 +nsVolumeService::FindVolumeByMountLockName(const nsAString& aMountLockName)
   1.301 +{
   1.302 +  MonitorAutoLock autoLock(mArrayMonitor);
   1.303 +
   1.304 +  nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   1.305 +  nsVolume::Array::index_type volIndex;
   1.306 +  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.307 +    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   1.308 +    nsString mountLockName;
   1.309 +    vol->GetMountLockName(mountLockName);
   1.310 +    if (mountLockName.Equals(aMountLockName)) {
   1.311 +      return vol.forget();
   1.312 +    }
   1.313 +  }
   1.314 +  return nullptr;
   1.315 +}
   1.316 +
   1.317 +already_AddRefed<nsVolume>
   1.318 +nsVolumeService::FindVolumeByName(const nsAString& aName)
   1.319 +{
   1.320 +  mArrayMonitor.AssertCurrentThreadOwns();
   1.321 +
   1.322 +  nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   1.323 +  nsVolume::Array::index_type volIndex;
   1.324 +  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
   1.325 +    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
   1.326 +    if (vol->Name().Equals(aName)) {
   1.327 +      return vol.forget();
   1.328 +    }
   1.329 +  }
   1.330 +  return nullptr;
   1.331 +}
   1.332 +
   1.333 +//static
   1.334 +already_AddRefed<nsVolume>
   1.335 +nsVolumeService::CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake /*= false*/)
   1.336 +{
   1.337 +  MonitorAutoLock autoLock(mArrayMonitor);
   1.338 +
   1.339 +  nsRefPtr<nsVolume> vol;
   1.340 +  vol = FindVolumeByName(aName);
   1.341 +  if (vol) {
   1.342 +    return vol.forget();
   1.343 +  }
   1.344 +  // Volume not found - add a new one
   1.345 +  vol = new nsVolume(aName);
   1.346 +  vol->SetIsFake(aIsFake);
   1.347 +  mVolumeArray.AppendElement(vol);
   1.348 +  return vol.forget();
   1.349 +}
   1.350 +
   1.351 +void
   1.352 +nsVolumeService::UpdateVolume(nsIVolume* aVolume)
   1.353 +{
   1.354 +  MOZ_ASSERT(NS_IsMainThread());
   1.355 +
   1.356 +  nsString volName;
   1.357 +  aVolume->GetName(volName);
   1.358 +  bool aIsFake;
   1.359 +  aVolume->GetIsFake(&aIsFake);
   1.360 +  nsRefPtr<nsVolume> vol = CreateOrFindVolumeByName(volName, aIsFake);
   1.361 +  if (vol->Equals(aVolume)) {
   1.362 +    // Nothing has really changed. Don't bother telling anybody.
   1.363 +    return;
   1.364 +  }
   1.365 +
   1.366 +  if (!vol->IsFake() && aIsFake) {
   1.367 +    // Prevent an incoming fake volume from overriding an existing real volume.
   1.368 +    return;
   1.369 +  }
   1.370 +
   1.371 +  vol->Set(aVolume);
   1.372 +  nsCOMPtr<nsIObserverService> obs = GetObserverService();
   1.373 +  if (!obs) {
   1.374 +    return;
   1.375 +  }
   1.376 +  NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   1.377 +  obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   1.378 +}
   1.379 +
   1.380 +NS_IMETHODIMP
   1.381 +nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path)
   1.382 +{
   1.383 +  if (XRE_GetProcessType() == GeckoProcessType_Default) {
   1.384 +    nsRefPtr<nsVolume> vol = new nsVolume(name, path, nsIVolume::STATE_INIT,
   1.385 +                                          -1    /* mountGeneration */,
   1.386 +                                          true  /* isMediaPresent */,
   1.387 +                                          false /* isSharing */,
   1.388 +                                          false /* isFormatting */);
   1.389 +    vol->SetIsFake(true);
   1.390 +    vol->LogState();
   1.391 +    UpdateVolume(vol.get());
   1.392 +    return NS_OK;
   1.393 +  }
   1.394 +
   1.395 +  ContentChild::GetSingleton()->SendCreateFakeVolume(nsString(name), nsString(path));
   1.396 +  return NS_OK;
   1.397 +}
   1.398 +
   1.399 +NS_IMETHODIMP
   1.400 +nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state)
   1.401 +{
   1.402 +  if (XRE_GetProcessType() == GeckoProcessType_Default) {
   1.403 +    nsRefPtr<nsVolume> vol;
   1.404 +    {
   1.405 +      MonitorAutoLock autoLock(mArrayMonitor);
   1.406 +      vol = FindVolumeByName(name);
   1.407 +    }
   1.408 +    if (!vol || !vol->IsFake()) {
   1.409 +      return NS_ERROR_NOT_AVAILABLE;
   1.410 +    }
   1.411 +    vol->SetState(state);
   1.412 +    vol->LogState();
   1.413 +    UpdateVolume(vol.get());
   1.414 +    return NS_OK;
   1.415 +  }
   1.416 +
   1.417 +  ContentChild::GetSingleton()->SendSetFakeVolumeState(nsString(name), state);
   1.418 +  return NS_OK;
   1.419 +}
   1.420 +
   1.421 +/***************************************************************************
   1.422 +* The UpdateVolumeRunnable creates an nsVolume and updates the main thread
   1.423 +* data structure while running on the main thread.
   1.424 +*/
   1.425 +class UpdateVolumeRunnable : public nsRunnable
   1.426 +{
   1.427 +public:
   1.428 +  UpdateVolumeRunnable(nsVolumeService* aVolumeService, const Volume* aVolume)
   1.429 +    : mVolumeService(aVolumeService),
   1.430 +      mVolume(new nsVolume(aVolume))
   1.431 +  {
   1.432 +    MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.433 +  }
   1.434 +
   1.435 +  NS_IMETHOD Run()
   1.436 +  {
   1.437 +    MOZ_ASSERT(NS_IsMainThread());
   1.438 +    DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d "
   1.439 +        "media %d sharing %d formatting %d",
   1.440 +        mVolume->NameStr().get(), mVolume->StateStr(),
   1.441 +        mVolume->MountGeneration(), (int)mVolume->IsMountLocked(),
   1.442 +        (int)mVolume->IsMediaPresent(), mVolume->IsSharing(),
   1.443 +        mVolume->IsFormatting());
   1.444 +
   1.445 +    mVolumeService->UpdateVolume(mVolume);
   1.446 +    mVolumeService = nullptr;
   1.447 +    mVolume = nullptr;
   1.448 +    return NS_OK;
   1.449 +  }
   1.450 +
   1.451 +private:
   1.452 +  nsRefPtr<nsVolumeService> mVolumeService;
   1.453 +  nsRefPtr<nsVolume>        mVolume;
   1.454 +};
   1.455 +
   1.456 +void
   1.457 +nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
   1.458 +{
   1.459 +  DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d "
   1.460 +      "media %d sharing %d formatting %d",
   1.461 +      aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
   1.462 +      aVolume->MountGeneration(), (int)aVolume->IsMountLocked(),
   1.463 +      (int)aVolume->MediaPresent(), (int)aVolume->IsSharing(),
   1.464 +      (int)aVolume->IsFormatting());
   1.465 +  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   1.466 +  NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
   1.467 +}
   1.468 +
   1.469 +} // namespace system
   1.470 +} // namespace mozilla

mercurial