dom/system/gonk/VolumeManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 "VolumeManager.h"
michael@0 6
michael@0 7 #include "Volume.h"
michael@0 8 #include "VolumeCommand.h"
michael@0 9 #include "VolumeManagerLog.h"
michael@0 10 #include "VolumeServiceTest.h"
michael@0 11
michael@0 12 #include "nsWhitespaceTokenizer.h"
michael@0 13 #include "nsXULAppAPI.h"
michael@0 14
michael@0 15 #include "base/message_loop.h"
michael@0 16 #include "mozilla/Scoped.h"
michael@0 17 #include "mozilla/StaticPtr.h"
michael@0 18
michael@0 19 #include <android/log.h>
michael@0 20 #include <cutils/sockets.h>
michael@0 21 #include <fcntl.h>
michael@0 22 #include <sys/socket.h>
michael@0 23
michael@0 24 namespace mozilla {
michael@0 25 namespace system {
michael@0 26
michael@0 27 static StaticRefPtr<VolumeManager> sVolumeManager;
michael@0 28
michael@0 29 VolumeManager::STATE VolumeManager::mState = VolumeManager::UNINITIALIZED;
michael@0 30 VolumeManager::StateObserverList VolumeManager::mStateObserverList;
michael@0 31
michael@0 32 /***************************************************************************/
michael@0 33
michael@0 34 VolumeManager::VolumeManager()
michael@0 35 : LineWatcher('\0', kRcvBufSize),
michael@0 36 mSocket(-1),
michael@0 37 mCommandPending(false)
michael@0 38 {
michael@0 39 DBG("VolumeManager constructor called");
michael@0 40 }
michael@0 41
michael@0 42 VolumeManager::~VolumeManager()
michael@0 43 {
michael@0 44 }
michael@0 45
michael@0 46 //static
michael@0 47 size_t
michael@0 48 VolumeManager::NumVolumes()
michael@0 49 {
michael@0 50 if (!sVolumeManager) {
michael@0 51 return 0;
michael@0 52 }
michael@0 53 return sVolumeManager->mVolumeArray.Length();
michael@0 54 }
michael@0 55
michael@0 56 //static
michael@0 57 TemporaryRef<Volume>
michael@0 58 VolumeManager::GetVolume(size_t aIndex)
michael@0 59 {
michael@0 60 MOZ_ASSERT(aIndex < NumVolumes());
michael@0 61 return sVolumeManager->mVolumeArray[aIndex];
michael@0 62 }
michael@0 63
michael@0 64 //static
michael@0 65 VolumeManager::STATE
michael@0 66 VolumeManager::State()
michael@0 67 {
michael@0 68 return mState;
michael@0 69 }
michael@0 70
michael@0 71 //static
michael@0 72 const char *
michael@0 73 VolumeManager::StateStr(VolumeManager::STATE aState)
michael@0 74 {
michael@0 75 switch (aState) {
michael@0 76 case UNINITIALIZED: return "Uninitialized";
michael@0 77 case STARTING: return "Starting";
michael@0 78 case VOLUMES_READY: return "Volumes Ready";
michael@0 79 }
michael@0 80 return "???";
michael@0 81 }
michael@0 82
michael@0 83
michael@0 84 //static
michael@0 85 void
michael@0 86 VolumeManager::SetState(STATE aNewState)
michael@0 87 {
michael@0 88 if (mState != aNewState) {
michael@0 89 LOG("changing state from '%s' to '%s'",
michael@0 90 StateStr(mState), StateStr(aNewState));
michael@0 91 mState = aNewState;
michael@0 92 mStateObserverList.Broadcast(StateChangedEvent());
michael@0 93 }
michael@0 94 }
michael@0 95
michael@0 96 //static
michael@0 97 void
michael@0 98 VolumeManager::RegisterStateObserver(StateObserver* aObserver)
michael@0 99 {
michael@0 100 mStateObserverList.AddObserver(aObserver);
michael@0 101 }
michael@0 102
michael@0 103 //static
michael@0 104 void VolumeManager::UnregisterStateObserver(StateObserver* aObserver)
michael@0 105 {
michael@0 106 mStateObserverList.RemoveObserver(aObserver);
michael@0 107 }
michael@0 108
michael@0 109 //static
michael@0 110 TemporaryRef<Volume>
michael@0 111 VolumeManager::FindVolumeByName(const nsCSubstring& aName)
michael@0 112 {
michael@0 113 if (!sVolumeManager) {
michael@0 114 return nullptr;
michael@0 115 }
michael@0 116 VolumeArray::size_type numVolumes = NumVolumes();
michael@0 117 VolumeArray::index_type volIndex;
michael@0 118 for (volIndex = 0; volIndex < numVolumes; volIndex++) {
michael@0 119 RefPtr<Volume> vol = GetVolume(volIndex);
michael@0 120 if (vol->Name().Equals(aName)) {
michael@0 121 return vol;
michael@0 122 }
michael@0 123 }
michael@0 124 return nullptr;
michael@0 125 }
michael@0 126
michael@0 127 //static
michael@0 128 TemporaryRef<Volume>
michael@0 129 VolumeManager::FindAddVolumeByName(const nsCSubstring& aName)
michael@0 130 {
michael@0 131 RefPtr<Volume> vol = FindVolumeByName(aName);
michael@0 132 if (vol) {
michael@0 133 return vol;
michael@0 134 }
michael@0 135 // No volume found, create and add a new one.
michael@0 136 vol = new Volume(aName);
michael@0 137 sVolumeManager->mVolumeArray.AppendElement(vol);
michael@0 138 return vol;
michael@0 139 }
michael@0 140
michael@0 141 class VolumeListCallback : public VolumeResponseCallback
michael@0 142 {
michael@0 143 virtual void ResponseReceived(const VolumeCommand* aCommand)
michael@0 144 {
michael@0 145 switch (ResponseCode()) {
michael@0 146 case ResponseCode::VolumeListResult: {
michael@0 147 // Each line will look something like:
michael@0 148 //
michael@0 149 // sdcard /mnt/sdcard 1
michael@0 150 //
michael@0 151 // So for each volume that we get back, we update any volumes that
michael@0 152 // we have of the same name, or add new ones if they don't exist.
michael@0 153 nsCWhitespaceTokenizer tokenizer(ResponseStr());
michael@0 154 nsDependentCSubstring volName(tokenizer.nextToken());
michael@0 155 RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName);
michael@0 156 vol->HandleVoldResponse(ResponseCode(), tokenizer);
michael@0 157 break;
michael@0 158 }
michael@0 159
michael@0 160 case ResponseCode::CommandOkay: {
michael@0 161 // We've received the list of volumes. Tell anybody who
michael@0 162 // is listening that we're open for business.
michael@0 163 VolumeManager::SetState(VolumeManager::VOLUMES_READY);
michael@0 164 break;
michael@0 165 }
michael@0 166 }
michael@0 167 }
michael@0 168 };
michael@0 169
michael@0 170 bool
michael@0 171 VolumeManager::OpenSocket()
michael@0 172 {
michael@0 173 SetState(STARTING);
michael@0 174 if ((mSocket.rwget() = socket_local_client("vold",
michael@0 175 ANDROID_SOCKET_NAMESPACE_RESERVED,
michael@0 176 SOCK_STREAM)) < 0) {
michael@0 177 ERR("Error connecting to vold: (%s) - will retry", strerror(errno));
michael@0 178 return false;
michael@0 179 }
michael@0 180 // add FD_CLOEXEC flag
michael@0 181 int flags = fcntl(mSocket.get(), F_GETFD);
michael@0 182 if (flags == -1) {
michael@0 183 return false;
michael@0 184 }
michael@0 185 flags |= FD_CLOEXEC;
michael@0 186 if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
michael@0 187 return false;
michael@0 188 }
michael@0 189 // set non-blocking
michael@0 190 if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
michael@0 191 return false;
michael@0 192 }
michael@0 193 if (!MessageLoopForIO::current()->
michael@0 194 WatchFileDescriptor(mSocket.get(),
michael@0 195 true,
michael@0 196 MessageLoopForIO::WATCH_READ,
michael@0 197 &mReadWatcher,
michael@0 198 this)) {
michael@0 199 return false;
michael@0 200 }
michael@0 201
michael@0 202 LOG("Connected to vold");
michael@0 203 PostCommand(new VolumeListCommand(new VolumeListCallback));
michael@0 204 return true;
michael@0 205 }
michael@0 206
michael@0 207 //static
michael@0 208 void
michael@0 209 VolumeManager::PostCommand(VolumeCommand* aCommand)
michael@0 210 {
michael@0 211 if (!sVolumeManager) {
michael@0 212 ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data());
michael@0 213 return;
michael@0 214 }
michael@0 215 aCommand->SetPending(true);
michael@0 216
michael@0 217 DBG("Sending command '%s'", aCommand->Data());
michael@0 218 // vold can only process one command at a time, so add our command
michael@0 219 // to the end of the command queue.
michael@0 220 sVolumeManager->mCommands.push(aCommand);
michael@0 221 if (!sVolumeManager->mCommandPending) {
michael@0 222 // There aren't any commands currently being processed, so go
michael@0 223 // ahead and kick this one off.
michael@0 224 sVolumeManager->mCommandPending = true;
michael@0 225 sVolumeManager->WriteCommandData();
michael@0 226 }
michael@0 227 }
michael@0 228
michael@0 229 /***************************************************************************
michael@0 230 * The WriteCommandData initiates sending of a command to vold. Since
michael@0 231 * we're running on the IOThread and not allowed to block, WriteCommandData
michael@0 232 * will write as much data as it can, and if not all of the data can be
michael@0 233 * written then it will setup a file descriptor watcher and
michael@0 234 * OnFileCanWriteWithoutBlocking will call WriteCommandData to write out
michael@0 235 * more of the command data.
michael@0 236 */
michael@0 237 void
michael@0 238 VolumeManager::WriteCommandData()
michael@0 239 {
michael@0 240 if (mCommands.size() == 0) {
michael@0 241 return;
michael@0 242 }
michael@0 243
michael@0 244 VolumeCommand* cmd = mCommands.front();
michael@0 245 if (cmd->BytesRemaining() == 0) {
michael@0 246 // All bytes have been written. We're waiting for a response.
michael@0 247 return;
michael@0 248 }
michael@0 249 // There are more bytes left to write. Try to write them all.
michael@0 250 ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining());
michael@0 251 if (bytesWritten < 0) {
michael@0 252 ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining());
michael@0 253 Restart();
michael@0 254 return;
michael@0 255 }
michael@0 256 DBG("Wrote %ld bytes (of %d)", bytesWritten, cmd->BytesRemaining());
michael@0 257 cmd->ConsumeBytes(bytesWritten);
michael@0 258 if (cmd->BytesRemaining() == 0) {
michael@0 259 return;
michael@0 260 }
michael@0 261 // We were unable to write all of the command bytes. Setup a watcher
michael@0 262 // so we'll get called again when we can write without blocking.
michael@0 263 if (!MessageLoopForIO::current()->
michael@0 264 WatchFileDescriptor(mSocket.get(),
michael@0 265 false, // one-shot
michael@0 266 MessageLoopForIO::WATCH_WRITE,
michael@0 267 &mWriteWatcher,
michael@0 268 this)) {
michael@0 269 ERR("Failed to setup write watcher for vold socket");
michael@0 270 Restart();
michael@0 271 }
michael@0 272 }
michael@0 273
michael@0 274 void
michael@0 275 VolumeManager::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
michael@0 276 {
michael@0 277 MOZ_ASSERT(aFd == mSocket.get());
michael@0 278 char* endPtr;
michael@0 279 int responseCode = strtol(aMessage.Data(), &endPtr, 10);
michael@0 280 if (*endPtr == ' ') {
michael@0 281 endPtr++;
michael@0 282 }
michael@0 283
michael@0 284 // Now fish out the rest of the line after the response code
michael@0 285 nsDependentCString responseLine(endPtr, aMessage.Length() - (endPtr - aMessage.Data()));
michael@0 286 DBG("Rcvd: %d '%s'", responseCode, responseLine.Data());
michael@0 287
michael@0 288 if (responseCode >= ResponseCode::UnsolicitedInformational) {
michael@0 289 // These are unsolicited broadcasts. We intercept these and process
michael@0 290 // them ourselves
michael@0 291 HandleBroadcast(responseCode, responseLine);
michael@0 292 } else {
michael@0 293 // Everything else is considered to be part of the command response.
michael@0 294 if (mCommands.size() > 0) {
michael@0 295 VolumeCommand* cmd = mCommands.front();
michael@0 296 cmd->HandleResponse(responseCode, responseLine);
michael@0 297 if (responseCode >= ResponseCode::CommandOkay) {
michael@0 298 // That's a terminating response. We can remove the command.
michael@0 299 mCommands.pop();
michael@0 300 mCommandPending = false;
michael@0 301 // Start the next command, if there is one.
michael@0 302 WriteCommandData();
michael@0 303 }
michael@0 304 } else {
michael@0 305 ERR("Response with no command");
michael@0 306 }
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 void
michael@0 311 VolumeManager::OnFileCanWriteWithoutBlocking(int aFd)
michael@0 312 {
michael@0 313 MOZ_ASSERT(aFd == mSocket.get());
michael@0 314 WriteCommandData();
michael@0 315 }
michael@0 316
michael@0 317 void
michael@0 318 VolumeManager::HandleBroadcast(int aResponseCode, nsCString& aResponseLine)
michael@0 319 {
michael@0 320 // Format of the line is something like:
michael@0 321 //
michael@0 322 // Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
michael@0 323 //
michael@0 324 // So we parse out the volume name and the state after the string " to "
michael@0 325 nsCWhitespaceTokenizer tokenizer(aResponseLine);
michael@0 326 tokenizer.nextToken(); // The word "Volume"
michael@0 327 nsDependentCSubstring volName(tokenizer.nextToken());
michael@0 328
michael@0 329 RefPtr<Volume> vol = FindVolumeByName(volName);
michael@0 330 if (!vol) {
michael@0 331 return;
michael@0 332 }
michael@0 333 vol->HandleVoldResponse(aResponseCode, tokenizer);
michael@0 334 }
michael@0 335
michael@0 336 void
michael@0 337 VolumeManager::Restart()
michael@0 338 {
michael@0 339 mReadWatcher.StopWatchingFileDescriptor();
michael@0 340 mWriteWatcher.StopWatchingFileDescriptor();
michael@0 341
michael@0 342 while (!mCommands.empty()) {
michael@0 343 mCommands.pop();
michael@0 344 }
michael@0 345 mCommandPending = false;
michael@0 346 mSocket.dispose();
michael@0 347 Start();
michael@0 348 }
michael@0 349
michael@0 350 //static
michael@0 351 void
michael@0 352 VolumeManager::Start()
michael@0 353 {
michael@0 354 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 355
michael@0 356 if (!sVolumeManager) {
michael@0 357 return;
michael@0 358 }
michael@0 359 SetState(STARTING);
michael@0 360 if (!sVolumeManager->OpenSocket()) {
michael@0 361 // Socket open failed, try again in a second.
michael@0 362 MessageLoopForIO::current()->
michael@0 363 PostDelayedTask(FROM_HERE,
michael@0 364 NewRunnableFunction(VolumeManager::Start),
michael@0 365 1000);
michael@0 366 }
michael@0 367 }
michael@0 368
michael@0 369 void
michael@0 370 VolumeManager::OnError()
michael@0 371 {
michael@0 372 Restart();
michael@0 373 }
michael@0 374
michael@0 375 /***************************************************************************/
michael@0 376
michael@0 377 static void
michael@0 378 InitVolumeManagerIOThread()
michael@0 379 {
michael@0 380 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 381 MOZ_ASSERT(!sVolumeManager);
michael@0 382
michael@0 383 sVolumeManager = new VolumeManager();
michael@0 384 VolumeManager::Start();
michael@0 385
michael@0 386 InitVolumeServiceTestIOThread();
michael@0 387 }
michael@0 388
michael@0 389 static void
michael@0 390 ShutdownVolumeManagerIOThread()
michael@0 391 {
michael@0 392 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 393
michael@0 394 sVolumeManager = nullptr;
michael@0 395 }
michael@0 396
michael@0 397 /**************************************************************************
michael@0 398 *
michael@0 399 * Public API
michael@0 400 *
michael@0 401 * Since the VolumeManager runs in IO Thread context, we need to switch
michael@0 402 * to IOThread context before we can do anything.
michael@0 403 *
michael@0 404 **************************************************************************/
michael@0 405
michael@0 406 void
michael@0 407 InitVolumeManager()
michael@0 408 {
michael@0 409 XRE_GetIOMessageLoop()->PostTask(
michael@0 410 FROM_HERE,
michael@0 411 NewRunnableFunction(InitVolumeManagerIOThread));
michael@0 412 }
michael@0 413
michael@0 414 void
michael@0 415 ShutdownVolumeManager()
michael@0 416 {
michael@0 417 ShutdownVolumeServiceTest();
michael@0 418
michael@0 419 XRE_GetIOMessageLoop()->PostTask(
michael@0 420 FROM_HERE,
michael@0 421 NewRunnableFunction(ShutdownVolumeManagerIOThread));
michael@0 422 }
michael@0 423
michael@0 424 } // system
michael@0 425 } // mozilla

mercurial