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 "nsVolume.h" michael@0: michael@0: #include "base/message_loop.h" michael@0: #include "nsIPowerManagerService.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsIVolume.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsVolumeStat.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "Volume.h" michael@0: #include "AutoMounter.h" michael@0: #include "VolumeManager.h" michael@0: michael@0: #define VOLUME_MANAGER_LOG_TAG "nsVolume" michael@0: #include "VolumeManagerLog.h" michael@0: michael@0: namespace mozilla { michael@0: namespace system { michael@0: michael@0: const char * michael@0: NS_VolumeStateStr(int32_t aState) michael@0: { michael@0: switch (aState) { michael@0: case nsIVolume::STATE_INIT: return "Init"; michael@0: case nsIVolume::STATE_NOMEDIA: return "NoMedia"; michael@0: case nsIVolume::STATE_IDLE: return "Idle"; michael@0: case nsIVolume::STATE_PENDING: return "Pending"; michael@0: case nsIVolume::STATE_CHECKING: return "Checking"; michael@0: case nsIVolume::STATE_MOUNTED: return "Mounted"; michael@0: case nsIVolume::STATE_UNMOUNTING: return "Unmounting"; michael@0: case nsIVolume::STATE_FORMATTING: return "Formatting"; michael@0: case nsIVolume::STATE_SHARED: return "Shared"; michael@0: case nsIVolume::STATE_SHAREDMNT: return "Shared-Mounted"; michael@0: } michael@0: return "???"; michael@0: } michael@0: michael@0: // While nsVolumes can only be used on the main thread, in the michael@0: // UpdateVolumeRunnable constructor (which is called from IOThread) we michael@0: // allocate an nsVolume which is then passed to MainThread. Since we michael@0: // have a situation where we allocate on one thread and free on another michael@0: // we use a thread safe AddRef implementation. michael@0: NS_IMPL_ISUPPORTS(nsVolume, nsIVolume) michael@0: michael@0: nsVolume::nsVolume(const Volume* aVolume) michael@0: : mName(NS_ConvertUTF8toUTF16(aVolume->Name())), michael@0: mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())), michael@0: mState(aVolume->State()), michael@0: mMountGeneration(aVolume->MountGeneration()), michael@0: mMountLocked(aVolume->IsMountLocked()), michael@0: mIsFake(false), michael@0: mIsMediaPresent(aVolume->MediaPresent()), michael@0: mIsSharing(aVolume->IsSharing()), michael@0: mIsFormatting(aVolume->IsFormatting()) michael@0: { michael@0: } michael@0: michael@0: bool nsVolume::Equals(nsIVolume* aVolume) michael@0: { michael@0: nsString volName; michael@0: aVolume->GetName(volName); michael@0: if (!mName.Equals(volName)) { michael@0: return false; michael@0: } michael@0: michael@0: nsString volMountPoint; michael@0: aVolume->GetMountPoint(volMountPoint); michael@0: if (!mMountPoint.Equals(volMountPoint)) { michael@0: return false; michael@0: } michael@0: michael@0: int32_t volState; michael@0: aVolume->GetState(&volState); michael@0: if (mState != volState){ michael@0: return false; michael@0: } michael@0: michael@0: int32_t volMountGeneration; michael@0: aVolume->GetMountGeneration(&volMountGeneration); michael@0: if (mMountGeneration != volMountGeneration) { michael@0: return false; michael@0: } michael@0: michael@0: bool volIsMountLocked; michael@0: aVolume->GetIsMountLocked(&volIsMountLocked); michael@0: if (mMountLocked != volIsMountLocked) { michael@0: return false; michael@0: } michael@0: michael@0: bool isFake; michael@0: aVolume->GetIsFake(&isFake); michael@0: if (mIsFake != isFake) { michael@0: return false; michael@0: } michael@0: michael@0: bool isSharing; michael@0: aVolume->GetIsSharing(&isSharing); michael@0: if (mIsSharing != isSharing) { michael@0: return false; michael@0: } michael@0: michael@0: bool isFormatting; michael@0: aVolume->GetIsFormatting(&isFormatting); michael@0: if (mIsFormatting != isFormatting) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent) michael@0: { michael@0: *aIsMediaPresent = mIsMediaPresent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked) michael@0: { michael@0: *aIsMountLocked = mMountLocked; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing) michael@0: { michael@0: *aIsSharing = mIsSharing; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetIsFormatting(bool *aIsFormatting) michael@0: { michael@0: *aIsFormatting = mIsFormatting; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetName(nsAString& aName) michael@0: { michael@0: aName = mName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t* aMountGeneration) michael@0: { michael@0: *aMountGeneration = mMountGeneration; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetMountLockName(nsAString& aMountLockName) michael@0: { michael@0: aMountLockName = NS_LITERAL_STRING("volume-") + Name(); michael@0: aMountLockName.AppendPrintf("-%d", mMountGeneration); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetMountPoint(nsAString& aMountPoint) michael@0: { michael@0: aMountPoint = mMountPoint; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetState(int32_t* aState) michael@0: { michael@0: *aState = mState; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetStats(nsIVolumeStat **aResult) michael@0: { michael@0: if (mState != STATE_MOUNTED) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IF_ADDREF(*aResult = new nsVolumeStat(mMountPoint)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::GetIsFake(bool *aIsFake) michael@0: { michael@0: *aIsFake = mIsFake; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::Format() michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(FormatVolumeIOThread, NameStr())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); michael@0: if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { michael@0: return; michael@0: } michael@0: michael@0: AutoMounterFormatVolume(aVolume); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::Mount() michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(MountVolumeIOThread, NameStr())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void nsVolume::MountVolumeIOThread(const nsCString& aVolume) michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); michael@0: if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { michael@0: return; michael@0: } michael@0: michael@0: AutoMounterMountVolume(aVolume); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsVolume::Unmount() michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(UnmountVolumeIOThread, NameStr())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void nsVolume::UnmountVolumeIOThread(const nsCString& aVolume) michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: michael@0: MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); michael@0: if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { michael@0: return; michael@0: } michael@0: michael@0: AutoMounterUnmountVolume(aVolume); michael@0: } michael@0: michael@0: void michael@0: nsVolume::LogState() const michael@0: { michael@0: if (mState == nsIVolume::STATE_MOUNTED) { michael@0: LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d " michael@0: "media %d sharing %d formatting %d", michael@0: NameStr().get(), StateStr(), MountPointStr().get(), michael@0: MountGeneration(), (int)IsMountLocked(), (int)IsFake(), michael@0: (int)IsMediaPresent(), (int)IsSharing(), michael@0: (int)IsFormatting()); michael@0: return; michael@0: } michael@0: michael@0: LOG("nsVolume: %s state %s", NameStr().get(), StateStr()); michael@0: } michael@0: michael@0: void nsVolume::Set(nsIVolume* aVolume) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: aVolume->GetName(mName); michael@0: aVolume->GetMountPoint(mMountPoint); michael@0: aVolume->GetState(&mState); michael@0: aVolume->GetIsFake(&mIsFake); michael@0: aVolume->GetIsMediaPresent(&mIsMediaPresent); michael@0: aVolume->GetIsSharing(&mIsSharing); michael@0: aVolume->GetIsFormatting(&mIsFormatting); michael@0: michael@0: int32_t volMountGeneration; michael@0: aVolume->GetMountGeneration(&volMountGeneration); michael@0: michael@0: if (mState != nsIVolume::STATE_MOUNTED) { michael@0: // Since we're not in the mounted state, we need to michael@0: // forgot whatever mount generation we may have had. michael@0: mMountGeneration = -1; michael@0: return; michael@0: } michael@0: if (mMountGeneration == volMountGeneration) { michael@0: // No change in mount generation, nothing else to do michael@0: return; michael@0: } michael@0: michael@0: mMountGeneration = volMountGeneration; michael@0: michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: // Child processes just track the state, not maintain it. michael@0: aVolume->GetIsMountLocked(&mMountLocked); michael@0: return; michael@0: } michael@0: michael@0: // Notify the Volume on IOThread whether the volume is locked or not. michael@0: nsCOMPtr pmService = michael@0: do_GetService(POWERMANAGERSERVICE_CONTRACTID); michael@0: if (!pmService) { michael@0: return; michael@0: } michael@0: nsString mountLockName; michael@0: GetMountLockName(mountLockName); michael@0: nsString mountLockState; michael@0: pmService->GetWakeLockState(mountLockName, mountLockState); michael@0: UpdateMountLock(mountLockState); michael@0: } michael@0: michael@0: void michael@0: nsVolume::UpdateMountLock(const nsAString& aMountLockState) michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // There are 3 states, unlocked, locked-background, and locked-foreground michael@0: // I figured it was easier to use negtive logic and compare for unlocked. michael@0: UpdateMountLock(!aMountLockState.EqualsLiteral("unlocked")); michael@0: } michael@0: michael@0: void michael@0: nsVolume::UpdateMountLock(bool aMountLocked) michael@0: { michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (aMountLocked == mMountLocked) { michael@0: return; michael@0: } michael@0: // The locked/unlocked state changed. Tell IOThread about it. michael@0: mMountLocked = aMountLocked; michael@0: LogState(); michael@0: XRE_GetIOMessageLoop()->PostTask( michael@0: FROM_HERE, michael@0: NewRunnableFunction(Volume::UpdateMountLock, michael@0: NS_LossyConvertUTF16toASCII(Name()), michael@0: MountGeneration(), aMountLocked)); michael@0: } michael@0: michael@0: void michael@0: nsVolume::SetIsFake(bool aIsFake) michael@0: { michael@0: mIsFake = aIsFake; michael@0: if (mIsFake) { michael@0: // The media is always present for fake volumes. michael@0: mIsMediaPresent = true; michael@0: MOZ_ASSERT(!mIsSharing); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsVolume::SetState(int32_t aState) michael@0: { michael@0: static int32_t sMountGeneration = 0; michael@0: michael@0: MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(IsFake()); michael@0: michael@0: if (aState == mState) { michael@0: return; michael@0: } michael@0: michael@0: if (aState == nsIVolume::STATE_MOUNTED) { michael@0: mMountGeneration = ++sMountGeneration; michael@0: } michael@0: michael@0: mState = aState; michael@0: } michael@0: michael@0: } // system michael@0: } // mozilla