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

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

mercurial