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

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

mercurial