dom/system/gonk/Volume.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 "Volume.h"
michael@0 6 #include "VolumeCommand.h"
michael@0 7 #include "VolumeManager.h"
michael@0 8 #include "VolumeManagerLog.h"
michael@0 9 #include "nsIVolume.h"
michael@0 10 #include "nsXULAppAPI.h"
michael@0 11
michael@0 12 #include <vold/ResponseCode.h>
michael@0 13
michael@0 14 namespace mozilla {
michael@0 15 namespace system {
michael@0 16
michael@0 17 Volume::EventObserverList Volume::mEventObserverList;
michael@0 18
michael@0 19 // We have a feature where volumes can be locked when mounted. This
michael@0 20 // is used to prevent a volume from being shared with the PC while
michael@0 21 // it is actively being used (say for storing an update image)
michael@0 22 //
michael@0 23 // We use WakeLocks (a poor choice of name, but it does what we want)
michael@0 24 // from the PowerManagerService to determine when we're locked.
michael@0 25 // In particular we'll create a wakelock called volume-NAME-GENERATION
michael@0 26 // (where NAME is the volume name, and GENERATION is its generation
michael@0 27 // number), and if this wakelock is locked, then we'll prevent a volume
michael@0 28 // from being shared.
michael@0 29 //
michael@0 30 // Implementation Details:
michael@0 31 //
michael@0 32 // Since the AutoMounter can only control when something gets mounted
michael@0 33 // and not when it gets unmounted (for example: a user pulls the SDCard)
michael@0 34 // and because Volume and nsVolume data structures are maintained on
michael@0 35 // separate threads, we have the potential for some race conditions.
michael@0 36 // We eliminate the race conditions by introducing the concept of a
michael@0 37 // generation number. Every time a volume transitions to the Mounted
michael@0 38 // state, it gets assigned a new generation number. Whenever the state
michael@0 39 // of a Volume changes, we send the updated state and current generation
michael@0 40 // number to the main thread where it gets updated in the nsVolume.
michael@0 41 //
michael@0 42 // Since WakeLocks can only be queried from the main-thread, the
michael@0 43 // nsVolumeService looks for WakeLock status changes, and forwards
michael@0 44 // the results to the IOThread.
michael@0 45 //
michael@0 46 // If the Volume (IOThread) recieves a volume update where the generation
michael@0 47 // number mismatches, then the update is simply ignored.
michael@0 48 //
michael@0 49 // When a Volume (IOThread) initially becomes mounted, we assume it to
michael@0 50 // be locked until we get our first update from nsVolume (MainThread).
michael@0 51 static int32_t sMountGeneration = 0;
michael@0 52
michael@0 53 // We don't get media inserted/removed events at startup. So we
michael@0 54 // assume it's present, and we'll be told that it's missing.
michael@0 55 Volume::Volume(const nsCSubstring& aName)
michael@0 56 : mMediaPresent(true),
michael@0 57 mState(nsIVolume::STATE_INIT),
michael@0 58 mName(aName),
michael@0 59 mMountGeneration(-1),
michael@0 60 mMountLocked(true), // Needs to agree with nsVolume::nsVolume
michael@0 61 mSharingEnabled(false),
michael@0 62 mCanBeShared(true),
michael@0 63 mIsSharing(false),
michael@0 64 mFormatRequested(false),
michael@0 65 mMountRequested(false),
michael@0 66 mUnmountRequested(false),
michael@0 67 mIsFormatting(false)
michael@0 68 {
michael@0 69 DBG("Volume %s: created", NameStr());
michael@0 70 }
michael@0 71
michael@0 72 void
michael@0 73 Volume::SetIsSharing(bool aIsSharing)
michael@0 74 {
michael@0 75 if (aIsSharing == mIsSharing) {
michael@0 76 return;
michael@0 77 }
michael@0 78 mIsSharing = aIsSharing;
michael@0 79 LOG("Volume %s: IsSharing set to %d state %s",
michael@0 80 NameStr(), (int)mIsSharing, StateStr(mState));
michael@0 81 if (mIsSharing) {
michael@0 82 mEventObserverList.Broadcast(this);
michael@0 83 }
michael@0 84 }
michael@0 85
michael@0 86 void
michael@0 87 Volume::SetIsFormatting(bool aIsFormatting)
michael@0 88 {
michael@0 89 if (aIsFormatting == mIsFormatting) {
michael@0 90 return;
michael@0 91 }
michael@0 92 mIsFormatting = aIsFormatting;
michael@0 93 LOG("Volume %s: IsFormatting set to %d state %s",
michael@0 94 NameStr(), (int)mIsFormatting, StateStr(mState));
michael@0 95 if (mIsFormatting) {
michael@0 96 mEventObserverList.Broadcast(this);
michael@0 97 }
michael@0 98 }
michael@0 99
michael@0 100 void
michael@0 101 Volume::SetMediaPresent(bool aMediaPresent)
michael@0 102 {
michael@0 103 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 104 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 105
michael@0 106 // mMediaPresent is slightly redunant to the state, however
michael@0 107 // when media is removed (while Idle), we get the following:
michael@0 108 // 631 Volume sdcard /mnt/sdcard disk removed (179:0)
michael@0 109 // 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
michael@0 110 //
michael@0 111 // And on media insertion, we get:
michael@0 112 // 630 Volume sdcard /mnt/sdcard disk inserted (179:0)
michael@0 113 // 605 Volume sdcard /mnt/sdcard state changed from 0 (No-Media) to 2 (Pending)
michael@0 114 // 605 Volume sdcard /mnt/sdcard state changed from 2 (Pending) to 1 (Idle-Unmounted)
michael@0 115 //
michael@0 116 // On media removal while the media is mounted:
michael@0 117 // 632 Volume sdcard /mnt/sdcard bad removal (179:1)
michael@0 118 // 605 Volume sdcard /mnt/sdcard state changed from 4 (Mounted) to 5 (Unmounting)
michael@0 119 // 605 Volume sdcard /mnt/sdcard state changed from 5 (Unmounting) to 1 (Idle-Unmounted)
michael@0 120 // 631 Volume sdcard /mnt/sdcard disk removed (179:0)
michael@0 121 // 605 Volume sdcard /mnt/sdcard state changed from 1 (Idle-Unmounted) to 0 (No-Media)
michael@0 122 //
michael@0 123 // When sharing with a PC, it goes Mounted -> Idle -> Shared
michael@0 124 // When unsharing with a PC, it goes Shared -> Idle -> Mounted
michael@0 125 //
michael@0 126 // The AutoMounter needs to know whether the media is present or not when
michael@0 127 // processing the Idle state.
michael@0 128
michael@0 129 if (mMediaPresent == aMediaPresent) {
michael@0 130 return;
michael@0 131 }
michael@0 132
michael@0 133 LOG("Volume: %s media %s", NameStr(), aMediaPresent ? "inserted" : "removed");
michael@0 134 mMediaPresent = aMediaPresent;
michael@0 135 mEventObserverList.Broadcast(this);
michael@0 136 }
michael@0 137
michael@0 138 void
michael@0 139 Volume::SetSharingEnabled(bool aSharingEnabled)
michael@0 140 {
michael@0 141 mSharingEnabled = aSharingEnabled;
michael@0 142
michael@0 143 LOG("SetSharingMode for volume %s to %d canBeShared = %d",
michael@0 144 NameStr(), (int)mSharingEnabled, (int)mCanBeShared);
michael@0 145 }
michael@0 146
michael@0 147 void
michael@0 148 Volume::SetFormatRequested(bool aFormatRequested)
michael@0 149 {
michael@0 150 mFormatRequested = aFormatRequested;
michael@0 151
michael@0 152 LOG("SetFormatRequested for volume %s to %d CanBeFormatted = %d",
michael@0 153 NameStr(), (int)mFormatRequested, (int)CanBeFormatted());
michael@0 154 }
michael@0 155
michael@0 156 void
michael@0 157 Volume::SetMountRequested(bool aMountRequested)
michael@0 158 {
michael@0 159 mMountRequested = aMountRequested;
michael@0 160
michael@0 161 LOG("SetMountRequested for volume %s to %d CanBeMounted = %d",
michael@0 162 NameStr(), (int)mMountRequested, (int)CanBeMounted());
michael@0 163 }
michael@0 164
michael@0 165 void
michael@0 166 Volume::SetUnmountRequested(bool aUnmountRequested)
michael@0 167 {
michael@0 168 mUnmountRequested = aUnmountRequested;
michael@0 169
michael@0 170 LOG("SetUnmountRequested for volume %s to %d CanBeMounted = %d",
michael@0 171 NameStr(), (int)mUnmountRequested, (int)CanBeMounted());
michael@0 172 }
michael@0 173
michael@0 174 void
michael@0 175 Volume::SetState(Volume::STATE aNewState)
michael@0 176 {
michael@0 177 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 178 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 179 if (aNewState == mState) {
michael@0 180 return;
michael@0 181 }
michael@0 182 if (aNewState == nsIVolume::STATE_MOUNTED) {
michael@0 183 mMountGeneration = ++sMountGeneration;
michael@0 184 LOG("Volume %s: changing state from %s to %s @ '%s' (%d observers) "
michael@0 185 "mountGeneration = %d, locked = %d",
michael@0 186 NameStr(), StateStr(mState),
michael@0 187 StateStr(aNewState), mMountPoint.get(), mEventObserverList.Length(),
michael@0 188 mMountGeneration, (int)mMountLocked);
michael@0 189 } else {
michael@0 190 LOG("Volume %s: changing state from %s to %s (%d observers)",
michael@0 191 NameStr(), StateStr(mState),
michael@0 192 StateStr(aNewState), mEventObserverList.Length());
michael@0 193 }
michael@0 194
michael@0 195 switch (aNewState) {
michael@0 196 case nsIVolume::STATE_NOMEDIA:
michael@0 197 // Cover the startup case where we don't get insertion/removal events
michael@0 198 mMediaPresent = false;
michael@0 199 mIsSharing = false;
michael@0 200 mUnmountRequested = false;
michael@0 201 mMountRequested = false;
michael@0 202 break;
michael@0 203
michael@0 204 case nsIVolume::STATE_MOUNTED:
michael@0 205 mMountRequested = false;
michael@0 206 mIsFormatting = false;
michael@0 207 mIsSharing = false;
michael@0 208 break;
michael@0 209 case nsIVolume::STATE_FORMATTING:
michael@0 210 mFormatRequested = false;
michael@0 211 mIsFormatting = true;
michael@0 212 mIsSharing = false;
michael@0 213 break;
michael@0 214
michael@0 215 case nsIVolume::STATE_SHARED:
michael@0 216 case nsIVolume::STATE_SHAREDMNT:
michael@0 217 // Covers startup cases. Normally, mIsSharing would be set to true
michael@0 218 // when we issue the command to initiate the sharing process, but
michael@0 219 // it's conceivable that a volume could already be in a shared state
michael@0 220 // when b2g starts.
michael@0 221 mIsSharing = true;
michael@0 222 break;
michael@0 223
michael@0 224 case nsIVolume::STATE_IDLE:
michael@0 225 break;
michael@0 226 default:
michael@0 227 break;
michael@0 228 }
michael@0 229 mState = aNewState;
michael@0 230 mEventObserverList.Broadcast(this);
michael@0 231 }
michael@0 232
michael@0 233 void
michael@0 234 Volume::SetMountPoint(const nsCSubstring& aMountPoint)
michael@0 235 {
michael@0 236 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 237 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 238
michael@0 239 if (mMountPoint.Equals(aMountPoint)) {
michael@0 240 return;
michael@0 241 }
michael@0 242 mMountPoint = aMountPoint;
michael@0 243 DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get());
michael@0 244 }
michael@0 245
michael@0 246 void
michael@0 247 Volume::StartMount(VolumeResponseCallback* aCallback)
michael@0 248 {
michael@0 249 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 250 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 251
michael@0 252 StartCommand(new VolumeActionCommand(this, "mount", "", aCallback));
michael@0 253 }
michael@0 254
michael@0 255 void
michael@0 256 Volume::StartUnmount(VolumeResponseCallback* aCallback)
michael@0 257 {
michael@0 258 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 259 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 260
michael@0 261 StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback));
michael@0 262 }
michael@0 263
michael@0 264 void
michael@0 265 Volume::StartFormat(VolumeResponseCallback* aCallback)
michael@0 266 {
michael@0 267 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 268 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 269
michael@0 270 StartCommand(new VolumeActionCommand(this, "format", "", aCallback));
michael@0 271 }
michael@0 272
michael@0 273 void
michael@0 274 Volume::StartShare(VolumeResponseCallback* aCallback)
michael@0 275 {
michael@0 276 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 277 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 278
michael@0 279 StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback));
michael@0 280 }
michael@0 281
michael@0 282 void
michael@0 283 Volume::StartUnshare(VolumeResponseCallback* aCallback)
michael@0 284 {
michael@0 285 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 286 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 287
michael@0 288 StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback));
michael@0 289 }
michael@0 290
michael@0 291 void
michael@0 292 Volume::StartCommand(VolumeCommand* aCommand)
michael@0 293 {
michael@0 294 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 295 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 296
michael@0 297 VolumeManager::PostCommand(aCommand);
michael@0 298 }
michael@0 299
michael@0 300 //static
michael@0 301 void
michael@0 302 Volume::RegisterObserver(Volume::EventObserver* aObserver)
michael@0 303 {
michael@0 304 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 305 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 306
michael@0 307 mEventObserverList.AddObserver(aObserver);
michael@0 308 // Send an initial event to the observer (for each volume)
michael@0 309 size_t numVolumes = VolumeManager::NumVolumes();
michael@0 310 for (size_t volIndex = 0; volIndex < numVolumes; volIndex++) {
michael@0 311 RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
michael@0 312 aObserver->Notify(vol);
michael@0 313 }
michael@0 314 }
michael@0 315
michael@0 316 //static
michael@0 317 void
michael@0 318 Volume::UnregisterObserver(Volume::EventObserver* aObserver)
michael@0 319 {
michael@0 320 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 321 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 322
michael@0 323 mEventObserverList.RemoveObserver(aObserver);
michael@0 324 }
michael@0 325
michael@0 326 //static
michael@0 327 void
michael@0 328 Volume::UpdateMountLock(const nsACString& aVolumeName,
michael@0 329 const int32_t& aMountGeneration,
michael@0 330 const bool& aMountLocked)
michael@0 331 {
michael@0 332 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 333 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 334
michael@0 335 RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
michael@0 336 if (!vol || (vol->mMountGeneration != aMountGeneration)) {
michael@0 337 return;
michael@0 338 }
michael@0 339 if (vol->mMountLocked != aMountLocked) {
michael@0 340 vol->mMountLocked = aMountLocked;
michael@0 341 DBG("Volume::UpdateMountLock for '%s' to %d\n", vol->NameStr(), (int)aMountLocked);
michael@0 342 mEventObserverList.Broadcast(vol);
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 void
michael@0 347 Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer)
michael@0 348 {
michael@0 349 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
michael@0 350 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 351
michael@0 352 // The volume name will have already been parsed, and the tokenizer will point
michael@0 353 // to the token after the volume name
michael@0 354 switch (aResponseCode) {
michael@0 355 case ResponseCode::VolumeListResult: {
michael@0 356 // Each line will look something like:
michael@0 357 //
michael@0 358 // sdcard /mnt/sdcard 1
michael@0 359 //
michael@0 360 nsDependentCSubstring mntPoint(aTokenizer.nextToken());
michael@0 361 SetMountPoint(mntPoint);
michael@0 362 nsresult errCode;
michael@0 363 nsCString state(aTokenizer.nextToken());
michael@0 364 if (state.EqualsLiteral("X")) {
michael@0 365 // Special state for creating fake volumes which can't be shared.
michael@0 366 mCanBeShared = false;
michael@0 367 SetState(nsIVolume::STATE_MOUNTED);
michael@0 368 } else {
michael@0 369 SetState((STATE)state.ToInteger(&errCode));
michael@0 370 }
michael@0 371 break;
michael@0 372 }
michael@0 373
michael@0 374 case ResponseCode::VolumeStateChange: {
michael@0 375 // Format of the line looks something like:
michael@0 376 //
michael@0 377 // Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
michael@0 378 //
michael@0 379 // So we parse out the state after the string " to "
michael@0 380 while (aTokenizer.hasMoreTokens()) {
michael@0 381 nsAutoCString token(aTokenizer.nextToken());
michael@0 382 if (token.Equals("to")) {
michael@0 383 nsresult errCode;
michael@0 384 token = aTokenizer.nextToken();
michael@0 385 SetState((STATE)token.ToInteger(&errCode));
michael@0 386 break;
michael@0 387 }
michael@0 388 }
michael@0 389 break;
michael@0 390 }
michael@0 391
michael@0 392 case ResponseCode::VolumeDiskInserted:
michael@0 393 SetMediaPresent(true);
michael@0 394 break;
michael@0 395
michael@0 396 case ResponseCode::VolumeDiskRemoved: // fall-thru
michael@0 397 case ResponseCode::VolumeBadRemoval:
michael@0 398 SetMediaPresent(false);
michael@0 399 break;
michael@0 400
michael@0 401 default:
michael@0 402 LOG("Volume: %s unrecognized reponse code (ignored)", NameStr());
michael@0 403 break;
michael@0 404 }
michael@0 405 }
michael@0 406
michael@0 407 } // namespace system
michael@0 408 } // namespace mozilla

mercurial