michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsVolumeMountLock.h" michael@0: michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPowerManagerService.h" michael@0: #include "nsIVolume.h" michael@0: #include "nsIVolumeService.h" michael@0: #include "nsString.h" michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #define VOLUME_MANAGER_LOG_TAG "nsVolumeMountLock" michael@0: #include "VolumeManagerLog.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "mozilla/dom/power/PowerManagerService.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::services; michael@0: michael@0: namespace mozilla { michael@0: namespace system { michael@0: michael@0: NS_IMPL_ISUPPORTS(nsVolumeMountLock, nsIVolumeMountLock, michael@0: nsIObserver, nsISupportsWeakReference) michael@0: michael@0: // static michael@0: already_AddRefed michael@0: nsVolumeMountLock::Create(const nsAString& aVolumeName) michael@0: { michael@0: DBG("nsVolumeMountLock::Create called"); michael@0: michael@0: nsRefPtr mountLock = new nsVolumeMountLock(aVolumeName); michael@0: nsresult rv = mountLock->Init(); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return mountLock.forget(); michael@0: } michael@0: michael@0: nsVolumeMountLock::nsVolumeMountLock(const nsAString& aVolumeName) michael@0: : mVolumeName(aVolumeName), michael@0: mVolumeGeneration(-1), michael@0: mUnlocked(false) michael@0: { michael@0: } michael@0: michael@0: //virtual michael@0: nsVolumeMountLock::~nsVolumeMountLock() michael@0: { michael@0: Unlock(); michael@0: } michael@0: michael@0: nsresult nsVolumeMountLock::Init() michael@0: { michael@0: LOG("nsVolumeMountLock created for '%s'", michael@0: NS_LossyConvertUTF16toASCII(mVolumeName).get()); michael@0: michael@0: // Add ourselves as an Observer. It's important that we use a weak michael@0: // reference here. If we used a strong reference, then that reference michael@0: // would prevent this object from being destructed. michael@0: nsCOMPtr obs = GetObserverService(); michael@0: obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, true /*weak*/); michael@0: michael@0: // Request the sdcard info, so we know the state/generation without having michael@0: // to wait for a state change. michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild::GetSingleton()->SendBroadcastVolume(mVolumeName); michael@0: return NS_OK; michael@0: } michael@0: nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE); michael@0: michael@0: vs->BroadcastVolume(mVolumeName); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void unlock (); */ michael@0: NS_IMETHODIMP nsVolumeMountLock::Unlock() michael@0: { michael@0: LOG("nsVolumeMountLock released for '%s'", michael@0: NS_LossyConvertUTF16toASCII(mVolumeName).get()); michael@0: michael@0: mUnlocked = true; michael@0: mWakeLock = nullptr; michael@0: michael@0: // While we don't really need to remove weak observers, we do so anyways michael@0: // since it will reduce the number of times Observe gets called. michael@0: nsCOMPtr obs = GetObserverService(); michael@0: obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolumeMountLock::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) michael@0: { michael@0: if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) { michael@0: return NS_OK; michael@0: } michael@0: if (mUnlocked) { michael@0: // We're not locked anymore, so we don't need to look at the notifications. michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr vol = do_QueryInterface(aSubject); michael@0: if (!vol) { michael@0: return NS_OK; michael@0: } michael@0: nsString volName; michael@0: vol->GetName(volName); michael@0: if (!volName.Equals(mVolumeName)) { michael@0: return NS_OK; michael@0: } michael@0: int32_t state; michael@0: nsresult rv = vol->GetState(&state); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (state != nsIVolume::STATE_MOUNTED) { michael@0: mWakeLock = nullptr; michael@0: mVolumeGeneration = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t mountGeneration; michael@0: rv = vol->GetMountGeneration(&mountGeneration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: DBG("nsVolumeMountLock::Observe mountGeneration = %d mVolumeGeneration = %d", michael@0: mountGeneration, mVolumeGeneration); michael@0: michael@0: if (mVolumeGeneration == mountGeneration) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // The generation changed, which means that any wakelock we may have michael@0: // been holding is now invalid. Grab a new wakelock for the new generation michael@0: // number. michael@0: michael@0: mWakeLock = nullptr; michael@0: mVolumeGeneration = mountGeneration; michael@0: michael@0: nsRefPtr pmService = michael@0: power::PowerManagerService::GetInstance(); michael@0: NS_ENSURE_TRUE(pmService, NS_ERROR_FAILURE); michael@0: michael@0: nsString mountLockName; michael@0: vol->GetMountLockName(mountLockName); michael@0: michael@0: ErrorResult err; michael@0: mWakeLock = pmService->NewWakeLock(mountLockName, nullptr, err); michael@0: if (err.Failed()) { michael@0: return err.ErrorCode(); michael@0: } michael@0: michael@0: LOG("nsVolumeMountLock acquired for '%s' gen %d", michael@0: NS_LossyConvertUTF16toASCII(mVolumeName).get(), mVolumeGeneration); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace system michael@0: } // namespace mozilla