|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "nsVolume.h" |
|
6 |
|
7 #include "base/message_loop.h" |
|
8 #include "nsIPowerManagerService.h" |
|
9 #include "nsISupportsUtils.h" |
|
10 #include "nsIVolume.h" |
|
11 #include "nsServiceManagerUtils.h" |
|
12 #include "nsThreadUtils.h" |
|
13 #include "nsVolumeStat.h" |
|
14 #include "nsXULAppAPI.h" |
|
15 #include "Volume.h" |
|
16 #include "AutoMounter.h" |
|
17 #include "VolumeManager.h" |
|
18 |
|
19 #define VOLUME_MANAGER_LOG_TAG "nsVolume" |
|
20 #include "VolumeManagerLog.h" |
|
21 |
|
22 namespace mozilla { |
|
23 namespace system { |
|
24 |
|
25 const char * |
|
26 NS_VolumeStateStr(int32_t aState) |
|
27 { |
|
28 switch (aState) { |
|
29 case nsIVolume::STATE_INIT: return "Init"; |
|
30 case nsIVolume::STATE_NOMEDIA: return "NoMedia"; |
|
31 case nsIVolume::STATE_IDLE: return "Idle"; |
|
32 case nsIVolume::STATE_PENDING: return "Pending"; |
|
33 case nsIVolume::STATE_CHECKING: return "Checking"; |
|
34 case nsIVolume::STATE_MOUNTED: return "Mounted"; |
|
35 case nsIVolume::STATE_UNMOUNTING: return "Unmounting"; |
|
36 case nsIVolume::STATE_FORMATTING: return "Formatting"; |
|
37 case nsIVolume::STATE_SHARED: return "Shared"; |
|
38 case nsIVolume::STATE_SHAREDMNT: return "Shared-Mounted"; |
|
39 } |
|
40 return "???"; |
|
41 } |
|
42 |
|
43 // While nsVolumes can only be used on the main thread, in the |
|
44 // UpdateVolumeRunnable constructor (which is called from IOThread) we |
|
45 // allocate an nsVolume which is then passed to MainThread. Since we |
|
46 // have a situation where we allocate on one thread and free on another |
|
47 // we use a thread safe AddRef implementation. |
|
48 NS_IMPL_ISUPPORTS(nsVolume, nsIVolume) |
|
49 |
|
50 nsVolume::nsVolume(const Volume* aVolume) |
|
51 : mName(NS_ConvertUTF8toUTF16(aVolume->Name())), |
|
52 mMountPoint(NS_ConvertUTF8toUTF16(aVolume->MountPoint())), |
|
53 mState(aVolume->State()), |
|
54 mMountGeneration(aVolume->MountGeneration()), |
|
55 mMountLocked(aVolume->IsMountLocked()), |
|
56 mIsFake(false), |
|
57 mIsMediaPresent(aVolume->MediaPresent()), |
|
58 mIsSharing(aVolume->IsSharing()), |
|
59 mIsFormatting(aVolume->IsFormatting()) |
|
60 { |
|
61 } |
|
62 |
|
63 bool nsVolume::Equals(nsIVolume* aVolume) |
|
64 { |
|
65 nsString volName; |
|
66 aVolume->GetName(volName); |
|
67 if (!mName.Equals(volName)) { |
|
68 return false; |
|
69 } |
|
70 |
|
71 nsString volMountPoint; |
|
72 aVolume->GetMountPoint(volMountPoint); |
|
73 if (!mMountPoint.Equals(volMountPoint)) { |
|
74 return false; |
|
75 } |
|
76 |
|
77 int32_t volState; |
|
78 aVolume->GetState(&volState); |
|
79 if (mState != volState){ |
|
80 return false; |
|
81 } |
|
82 |
|
83 int32_t volMountGeneration; |
|
84 aVolume->GetMountGeneration(&volMountGeneration); |
|
85 if (mMountGeneration != volMountGeneration) { |
|
86 return false; |
|
87 } |
|
88 |
|
89 bool volIsMountLocked; |
|
90 aVolume->GetIsMountLocked(&volIsMountLocked); |
|
91 if (mMountLocked != volIsMountLocked) { |
|
92 return false; |
|
93 } |
|
94 |
|
95 bool isFake; |
|
96 aVolume->GetIsFake(&isFake); |
|
97 if (mIsFake != isFake) { |
|
98 return false; |
|
99 } |
|
100 |
|
101 bool isSharing; |
|
102 aVolume->GetIsSharing(&isSharing); |
|
103 if (mIsSharing != isSharing) { |
|
104 return false; |
|
105 } |
|
106 |
|
107 bool isFormatting; |
|
108 aVolume->GetIsFormatting(&isFormatting); |
|
109 if (mIsFormatting != isFormatting) { |
|
110 return false; |
|
111 } |
|
112 |
|
113 return true; |
|
114 } |
|
115 |
|
116 NS_IMETHODIMP nsVolume::GetIsMediaPresent(bool *aIsMediaPresent) |
|
117 { |
|
118 *aIsMediaPresent = mIsMediaPresent; |
|
119 return NS_OK; |
|
120 } |
|
121 |
|
122 NS_IMETHODIMP nsVolume::GetIsMountLocked(bool *aIsMountLocked) |
|
123 { |
|
124 *aIsMountLocked = mMountLocked; |
|
125 return NS_OK; |
|
126 } |
|
127 |
|
128 NS_IMETHODIMP nsVolume::GetIsSharing(bool *aIsSharing) |
|
129 { |
|
130 *aIsSharing = mIsSharing; |
|
131 return NS_OK; |
|
132 } |
|
133 |
|
134 NS_IMETHODIMP nsVolume::GetIsFormatting(bool *aIsFormatting) |
|
135 { |
|
136 *aIsFormatting = mIsFormatting; |
|
137 return NS_OK; |
|
138 } |
|
139 |
|
140 NS_IMETHODIMP nsVolume::GetName(nsAString& aName) |
|
141 { |
|
142 aName = mName; |
|
143 return NS_OK; |
|
144 } |
|
145 |
|
146 NS_IMETHODIMP nsVolume::GetMountGeneration(int32_t* aMountGeneration) |
|
147 { |
|
148 *aMountGeneration = mMountGeneration; |
|
149 return NS_OK; |
|
150 } |
|
151 |
|
152 NS_IMETHODIMP nsVolume::GetMountLockName(nsAString& aMountLockName) |
|
153 { |
|
154 aMountLockName = NS_LITERAL_STRING("volume-") + Name(); |
|
155 aMountLockName.AppendPrintf("-%d", mMountGeneration); |
|
156 |
|
157 return NS_OK; |
|
158 } |
|
159 |
|
160 NS_IMETHODIMP nsVolume::GetMountPoint(nsAString& aMountPoint) |
|
161 { |
|
162 aMountPoint = mMountPoint; |
|
163 return NS_OK; |
|
164 } |
|
165 |
|
166 NS_IMETHODIMP nsVolume::GetState(int32_t* aState) |
|
167 { |
|
168 *aState = mState; |
|
169 return NS_OK; |
|
170 } |
|
171 |
|
172 NS_IMETHODIMP nsVolume::GetStats(nsIVolumeStat **aResult) |
|
173 { |
|
174 if (mState != STATE_MOUNTED) { |
|
175 return NS_ERROR_NOT_AVAILABLE; |
|
176 } |
|
177 |
|
178 NS_IF_ADDREF(*aResult = new nsVolumeStat(mMountPoint)); |
|
179 return NS_OK; |
|
180 } |
|
181 |
|
182 NS_IMETHODIMP nsVolume::GetIsFake(bool *aIsFake) |
|
183 { |
|
184 *aIsFake = mIsFake; |
|
185 return NS_OK; |
|
186 } |
|
187 |
|
188 NS_IMETHODIMP nsVolume::Format() |
|
189 { |
|
190 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
191 |
|
192 XRE_GetIOMessageLoop()->PostTask( |
|
193 FROM_HERE, |
|
194 NewRunnableFunction(FormatVolumeIOThread, NameStr())); |
|
195 |
|
196 return NS_OK; |
|
197 } |
|
198 |
|
199 /* static */ |
|
200 void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) |
|
201 { |
|
202 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
203 |
|
204 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
205 if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { |
|
206 return; |
|
207 } |
|
208 |
|
209 AutoMounterFormatVolume(aVolume); |
|
210 } |
|
211 |
|
212 NS_IMETHODIMP nsVolume::Mount() |
|
213 { |
|
214 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
215 |
|
216 XRE_GetIOMessageLoop()->PostTask( |
|
217 FROM_HERE, |
|
218 NewRunnableFunction(MountVolumeIOThread, NameStr())); |
|
219 |
|
220 return NS_OK; |
|
221 } |
|
222 |
|
223 /* static */ |
|
224 void nsVolume::MountVolumeIOThread(const nsCString& aVolume) |
|
225 { |
|
226 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
227 |
|
228 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
229 if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { |
|
230 return; |
|
231 } |
|
232 |
|
233 AutoMounterMountVolume(aVolume); |
|
234 } |
|
235 |
|
236 NS_IMETHODIMP nsVolume::Unmount() |
|
237 { |
|
238 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
239 |
|
240 XRE_GetIOMessageLoop()->PostTask( |
|
241 FROM_HERE, |
|
242 NewRunnableFunction(UnmountVolumeIOThread, NameStr())); |
|
243 |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 /* static */ |
|
248 void nsVolume::UnmountVolumeIOThread(const nsCString& aVolume) |
|
249 { |
|
250 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
251 |
|
252 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); |
|
253 if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { |
|
254 return; |
|
255 } |
|
256 |
|
257 AutoMounterUnmountVolume(aVolume); |
|
258 } |
|
259 |
|
260 void |
|
261 nsVolume::LogState() const |
|
262 { |
|
263 if (mState == nsIVolume::STATE_MOUNTED) { |
|
264 LOG("nsVolume: %s state %s @ '%s' gen %d locked %d fake %d " |
|
265 "media %d sharing %d formatting %d", |
|
266 NameStr().get(), StateStr(), MountPointStr().get(), |
|
267 MountGeneration(), (int)IsMountLocked(), (int)IsFake(), |
|
268 (int)IsMediaPresent(), (int)IsSharing(), |
|
269 (int)IsFormatting()); |
|
270 return; |
|
271 } |
|
272 |
|
273 LOG("nsVolume: %s state %s", NameStr().get(), StateStr()); |
|
274 } |
|
275 |
|
276 void nsVolume::Set(nsIVolume* aVolume) |
|
277 { |
|
278 MOZ_ASSERT(NS_IsMainThread()); |
|
279 |
|
280 aVolume->GetName(mName); |
|
281 aVolume->GetMountPoint(mMountPoint); |
|
282 aVolume->GetState(&mState); |
|
283 aVolume->GetIsFake(&mIsFake); |
|
284 aVolume->GetIsMediaPresent(&mIsMediaPresent); |
|
285 aVolume->GetIsSharing(&mIsSharing); |
|
286 aVolume->GetIsFormatting(&mIsFormatting); |
|
287 |
|
288 int32_t volMountGeneration; |
|
289 aVolume->GetMountGeneration(&volMountGeneration); |
|
290 |
|
291 if (mState != nsIVolume::STATE_MOUNTED) { |
|
292 // Since we're not in the mounted state, we need to |
|
293 // forgot whatever mount generation we may have had. |
|
294 mMountGeneration = -1; |
|
295 return; |
|
296 } |
|
297 if (mMountGeneration == volMountGeneration) { |
|
298 // No change in mount generation, nothing else to do |
|
299 return; |
|
300 } |
|
301 |
|
302 mMountGeneration = volMountGeneration; |
|
303 |
|
304 if (XRE_GetProcessType() != GeckoProcessType_Default) { |
|
305 // Child processes just track the state, not maintain it. |
|
306 aVolume->GetIsMountLocked(&mMountLocked); |
|
307 return; |
|
308 } |
|
309 |
|
310 // Notify the Volume on IOThread whether the volume is locked or not. |
|
311 nsCOMPtr<nsIPowerManagerService> pmService = |
|
312 do_GetService(POWERMANAGERSERVICE_CONTRACTID); |
|
313 if (!pmService) { |
|
314 return; |
|
315 } |
|
316 nsString mountLockName; |
|
317 GetMountLockName(mountLockName); |
|
318 nsString mountLockState; |
|
319 pmService->GetWakeLockState(mountLockName, mountLockState); |
|
320 UpdateMountLock(mountLockState); |
|
321 } |
|
322 |
|
323 void |
|
324 nsVolume::UpdateMountLock(const nsAString& aMountLockState) |
|
325 { |
|
326 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
327 MOZ_ASSERT(NS_IsMainThread()); |
|
328 |
|
329 // There are 3 states, unlocked, locked-background, and locked-foreground |
|
330 // I figured it was easier to use negtive logic and compare for unlocked. |
|
331 UpdateMountLock(!aMountLockState.EqualsLiteral("unlocked")); |
|
332 } |
|
333 |
|
334 void |
|
335 nsVolume::UpdateMountLock(bool aMountLocked) |
|
336 { |
|
337 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
338 MOZ_ASSERT(NS_IsMainThread()); |
|
339 |
|
340 if (aMountLocked == mMountLocked) { |
|
341 return; |
|
342 } |
|
343 // The locked/unlocked state changed. Tell IOThread about it. |
|
344 mMountLocked = aMountLocked; |
|
345 LogState(); |
|
346 XRE_GetIOMessageLoop()->PostTask( |
|
347 FROM_HERE, |
|
348 NewRunnableFunction(Volume::UpdateMountLock, |
|
349 NS_LossyConvertUTF16toASCII(Name()), |
|
350 MountGeneration(), aMountLocked)); |
|
351 } |
|
352 |
|
353 void |
|
354 nsVolume::SetIsFake(bool aIsFake) |
|
355 { |
|
356 mIsFake = aIsFake; |
|
357 if (mIsFake) { |
|
358 // The media is always present for fake volumes. |
|
359 mIsMediaPresent = true; |
|
360 MOZ_ASSERT(!mIsSharing); |
|
361 } |
|
362 } |
|
363 |
|
364 void |
|
365 nsVolume::SetState(int32_t aState) |
|
366 { |
|
367 static int32_t sMountGeneration = 0; |
|
368 |
|
369 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); |
|
370 MOZ_ASSERT(NS_IsMainThread()); |
|
371 MOZ_ASSERT(IsFake()); |
|
372 |
|
373 if (aState == mState) { |
|
374 return; |
|
375 } |
|
376 |
|
377 if (aState == nsIVolume::STATE_MOUNTED) { |
|
378 mMountGeneration = ++sMountGeneration; |
|
379 } |
|
380 |
|
381 mState = aState; |
|
382 } |
|
383 |
|
384 } // system |
|
385 } // mozilla |