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