summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/VolumeManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/system/gonk/VolumeManager.cpp')
-rw-r--r--dom/system/gonk/VolumeManager.cpp591
1 files changed, 0 insertions, 591 deletions
diff --git a/dom/system/gonk/VolumeManager.cpp b/dom/system/gonk/VolumeManager.cpp
deleted file mode 100644
index ddfa7af09..000000000
--- a/dom/system/gonk/VolumeManager.cpp
+++ /dev/null
@@ -1,591 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "VolumeManager.h"
-
-#include "Volume.h"
-#include "VolumeCommand.h"
-#include "VolumeManagerLog.h"
-#include "VolumeServiceTest.h"
-
-#include "nsWhitespaceTokenizer.h"
-#include "nsXULAppAPI.h"
-
-#include "base/message_loop.h"
-#include "base/task.h"
-#include "mozilla/Scoped.h"
-#include "mozilla/StaticPtr.h"
-
-#include <android/log.h>
-#include <cutils/sockets.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-
-namespace mozilla {
-namespace system {
-
-static StaticRefPtr<VolumeManager> sVolumeManager;
-
-VolumeManager::STATE VolumeManager::mState = VolumeManager::UNINITIALIZED;
-VolumeManager::StateObserverList VolumeManager::mStateObserverList;
-
-/***************************************************************************/
-
-VolumeManager::VolumeManager()
- : LineWatcher('\0', kRcvBufSize),
- mSocket(-1),
- mCommandPending(false)
-{
- DBG("VolumeManager constructor called");
-}
-
-VolumeManager::~VolumeManager()
-{
-}
-
-//static
-void
-VolumeManager::Dump(const char* aLabel)
-{
- if (!sVolumeManager) {
- LOG("%s: sVolumeManager == null", aLabel);
- return;
- }
-
- VolumeArray::size_type numVolumes = NumVolumes();
- VolumeArray::index_type volIndex;
- for (volIndex = 0; volIndex < numVolumes; volIndex++) {
- RefPtr<Volume> vol = GetVolume(volIndex);
- vol->Dump(aLabel);
- }
-}
-
-//static
-size_t
-VolumeManager::NumVolumes()
-{
- if (!sVolumeManager) {
- return 0;
- }
- return sVolumeManager->mVolumeArray.Length();
-}
-
-//static
-already_AddRefed<Volume>
-VolumeManager::GetVolume(size_t aIndex)
-{
- MOZ_ASSERT(aIndex < NumVolumes());
- RefPtr<Volume> vol = sVolumeManager->mVolumeArray[aIndex];
- return vol.forget();
-}
-
-//static
-VolumeManager::STATE
-VolumeManager::State()
-{
- return mState;
-}
-
-//static
-const char *
-VolumeManager::StateStr(VolumeManager::STATE aState)
-{
- switch (aState) {
- case UNINITIALIZED: return "Uninitialized";
- case STARTING: return "Starting";
- case VOLUMES_READY: return "Volumes Ready";
- }
- return "???";
-}
-
-
-//static
-void
-VolumeManager::SetState(STATE aNewState)
-{
- if (mState != aNewState) {
- LOG("changing state from '%s' to '%s'",
- StateStr(mState), StateStr(aNewState));
- mState = aNewState;
- mStateObserverList.Broadcast(StateChangedEvent());
- }
-}
-
-//static
-void
-VolumeManager::RegisterStateObserver(StateObserver* aObserver)
-{
- mStateObserverList.AddObserver(aObserver);
-}
-
-//static
-void VolumeManager::UnregisterStateObserver(StateObserver* aObserver)
-{
- mStateObserverList.RemoveObserver(aObserver);
-}
-
-//static
-already_AddRefed<Volume>
-VolumeManager::FindVolumeByName(const nsCSubstring& aName)
-{
- if (!sVolumeManager) {
- return nullptr;
- }
- VolumeArray::size_type numVolumes = NumVolumes();
- VolumeArray::index_type volIndex;
- for (volIndex = 0; volIndex < numVolumes; volIndex++) {
- RefPtr<Volume> vol = GetVolume(volIndex);
- if (vol->Name().Equals(aName)) {
- return vol.forget();
- }
- }
- return nullptr;
-}
-
-//static
-already_AddRefed<Volume>
-VolumeManager::FindAddVolumeByName(const nsCSubstring& aName)
-{
- RefPtr<Volume> vol = FindVolumeByName(aName);
- if (vol) {
- return vol.forget();
- }
- // No volume found, create and add a new one.
- vol = new Volume(aName);
- sVolumeManager->mVolumeArray.AppendElement(vol);
- return vol.forget();
-}
-
-//static
-bool
-VolumeManager::RemoveVolumeByName(const nsCSubstring& aName)
-{
- if (!sVolumeManager) {
- return false;
- }
- VolumeArray::size_type numVolumes = NumVolumes();
- VolumeArray::index_type volIndex;
- for (volIndex = 0; volIndex < numVolumes; volIndex++) {
- RefPtr<Volume> vol = GetVolume(volIndex);
- if (vol->Name().Equals(aName)) {
- sVolumeManager->mVolumeArray.RemoveElementAt(volIndex);
- return true;
- }
- }
- // No volume found. Return false to indicate this.
- return false;
-}
-
-
-//static
-void VolumeManager::InitConfig()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
-
- // This function uses /system/etc/volume.cfg to add additional volumes
- // to the Volume Manager.
- //
- // This is useful on devices like the Nexus 4, which have no physical sd card
- // or dedicated partition.
- //
- // The format of the volume.cfg file is as follows:
- // create volume-name mount-point
- // configure volume-name preference preference-value
- // Blank lines and lines starting with the hash character "#" will be ignored.
-
- ScopedCloseFile fp;
- int n = 0;
- char line[255];
- const char *filename = "/system/etc/volume.cfg";
- if (!(fp = fopen(filename, "r"))) {
- LOG("Unable to open volume configuration file '%s' - ignoring", filename);
- return;
- }
- while(fgets(line, sizeof(line), fp)) {
- n++;
-
- if (line[0] == '#')
- continue;
-
- nsCString commandline(line);
- nsCWhitespaceTokenizer tokenizer(commandline);
- if (!tokenizer.hasMoreTokens()) {
- // Blank line - ignore
- continue;
- }
-
- nsCString command(tokenizer.nextToken());
- if (command.EqualsLiteral("create")) {
- if (!tokenizer.hasMoreTokens()) {
- ERR("No vol_name in %s line %d", filename, n);
- continue;
- }
- nsCString volName(tokenizer.nextToken());
- if (!tokenizer.hasMoreTokens()) {
- ERR("No mount point for volume '%s'. %s line %d",
- volName.get(), filename, n);
- continue;
- }
- nsCString mountPoint(tokenizer.nextToken());
- RefPtr<Volume> vol = FindAddVolumeByName(volName);
- vol->SetFakeVolume(mountPoint);
- continue;
- }
- if (command.EqualsLiteral("configure")) {
- if (!tokenizer.hasMoreTokens()) {
- ERR("No vol_name in %s line %d", filename, n);
- continue;
- }
- nsCString volName(tokenizer.nextToken());
- if (!tokenizer.hasMoreTokens()) {
- ERR("No configuration name specified for volume '%s'. %s line %d",
- volName.get(), filename, n);
- continue;
- }
- nsCString configName(tokenizer.nextToken());
- if (!tokenizer.hasMoreTokens()) {
- ERR("No value for configuration name '%s'. %s line %d",
- configName.get(), filename, n);
- continue;
- }
- nsCString configValue(tokenizer.nextToken());
- RefPtr<Volume> vol = FindVolumeByName(volName);
- if (vol) {
- vol->SetConfig(configName, configValue);
- } else {
- ERR("Invalid volume name '%s'.", volName.get());
- }
- continue;
- }
- if (command.EqualsLiteral("ignore")) {
- // This command is useful to remove volumes which are being tracked by
- // vold, but for which we have no interest.
- if (!tokenizer.hasMoreTokens()) {
- ERR("No vol_name in %s line %d", filename, n);
- continue;
- }
- nsCString volName(tokenizer.nextToken());
- RemoveVolumeByName(volName);
- continue;
- }
- ERR("Unrecognized command: '%s'", command.get());
- }
-}
-
-void
-VolumeManager::DefaultConfig()
-{
-
- VolumeManager::VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
- if (numVolumes == 0) {
- return;
- }
- if (numVolumes == 1) {
- // This is to cover early shipping phones like the Buri,
- // which had no internal storage, and only external sdcard.
- //
- // Phones line the nexus-4 which only have an internal
- // storage area will need to have a volume.cfg file with
- // removable set to false.
- RefPtr<Volume> vol = VolumeManager::GetVolume(0);
- vol->SetIsRemovable(true);
- vol->SetIsHotSwappable(true);
- return;
- }
- VolumeManager::VolumeArray::index_type volIndex;
- for (volIndex = 0; volIndex < numVolumes; volIndex++) {
- RefPtr<Volume> vol = VolumeManager::GetVolume(volIndex);
- if (!vol->Name().EqualsLiteral("sdcard")) {
- vol->SetIsRemovable(true);
- vol->SetIsHotSwappable(true);
- }
- }
-}
-
-class VolumeListCallback : public VolumeResponseCallback
-{
- virtual void ResponseReceived(const VolumeCommand* aCommand)
- {
- switch (ResponseCode()) {
- case ::ResponseCode::VolumeListResult: {
- // Each line will look something like:
- //
- // sdcard /mnt/sdcard 1
- //
- // So for each volume that we get back, we update any volumes that
- // we have of the same name, or add new ones if they don't exist.
- nsCWhitespaceTokenizer tokenizer(ResponseStr());
- nsDependentCSubstring volName(tokenizer.nextToken());
- RefPtr<Volume> vol = VolumeManager::FindAddVolumeByName(volName);
- vol->HandleVoldResponse(ResponseCode(), tokenizer);
- break;
- }
-
- case ::ResponseCode::CommandOkay: {
- // We've received the list of volumes. Now read the Volume.cfg
- // file to perform customizations, and then tell everybody
- // that we're ready for business.
- VolumeManager::DefaultConfig();
- VolumeManager::InitConfig();
- VolumeManager::Dump("READY");
- VolumeManager::SetState(VolumeManager::VOLUMES_READY);
- break;
- }
- }
- }
-};
-
-bool
-VolumeManager::OpenSocket()
-{
- SetState(STARTING);
- if ((mSocket.rwget() = socket_local_client("vold",
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM)) < 0) {
- ERR("Error connecting to vold: (%s) - will retry", strerror(errno));
- return false;
- }
- // add FD_CLOEXEC flag
- int flags = fcntl(mSocket.get(), F_GETFD);
- if (flags == -1) {
- return false;
- }
- flags |= FD_CLOEXEC;
- if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
- return false;
- }
- // set non-blocking
- if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
- return false;
- }
- if (!MessageLoopForIO::current()->
- WatchFileDescriptor(mSocket.get(),
- true,
- MessageLoopForIO::WATCH_READ,
- &mReadWatcher,
- this)) {
- return false;
- }
-
- LOG("Connected to vold");
- PostCommand(new VolumeListCommand(new VolumeListCallback));
- return true;
-}
-
-//static
-void
-VolumeManager::PostCommand(VolumeCommand* aCommand)
-{
- if (!sVolumeManager) {
- ERR("VolumeManager not initialized. Dropping command '%s'", aCommand->Data());
- return;
- }
- aCommand->SetPending(true);
-
- DBG("Sending command '%s'", aCommand->Data());
- // vold can only process one command at a time, so add our command
- // to the end of the command queue.
- sVolumeManager->mCommands.push(aCommand);
- if (!sVolumeManager->mCommandPending) {
- // There aren't any commands currently being processed, so go
- // ahead and kick this one off.
- sVolumeManager->mCommandPending = true;
- sVolumeManager->WriteCommandData();
- }
-}
-
-/***************************************************************************
-* The WriteCommandData initiates sending of a command to vold. Since
-* we're running on the IOThread and not allowed to block, WriteCommandData
-* will write as much data as it can, and if not all of the data can be
-* written then it will setup a file descriptor watcher and
-* OnFileCanWriteWithoutBlocking will call WriteCommandData to write out
-* more of the command data.
-*/
-void
-VolumeManager::WriteCommandData()
-{
- if (mCommands.size() == 0) {
- return;
- }
-
- VolumeCommand* cmd = mCommands.front();
- if (cmd->BytesRemaining() == 0) {
- // All bytes have been written. We're waiting for a response.
- return;
- }
- // There are more bytes left to write. Try to write them all.
- ssize_t bytesWritten = write(mSocket.get(), cmd->Data(), cmd->BytesRemaining());
- if (bytesWritten < 0) {
- ERR("Failed to write %d bytes to vold socket", cmd->BytesRemaining());
- Restart();
- return;
- }
- DBG("Wrote %d bytes (of %d)", bytesWritten, cmd->BytesRemaining());
- cmd->ConsumeBytes(bytesWritten);
- if (cmd->BytesRemaining() == 0) {
- return;
- }
- // We were unable to write all of the command bytes. Setup a watcher
- // so we'll get called again when we can write without blocking.
- if (!MessageLoopForIO::current()->
- WatchFileDescriptor(mSocket.get(),
- false, // one-shot
- MessageLoopForIO::WATCH_WRITE,
- &mWriteWatcher,
- this)) {
- ERR("Failed to setup write watcher for vold socket");
- Restart();
- }
-}
-
-void
-VolumeManager::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
-{
- MOZ_ASSERT(aFd == mSocket.get());
- char* endPtr;
- int responseCode = strtol(aMessage.Data(), &endPtr, 10);
- if (*endPtr == ' ') {
- endPtr++;
- }
-
- // Now fish out the rest of the line after the response code
- nsDependentCString responseLine(endPtr, aMessage.Length() - (endPtr - aMessage.Data()));
- DBG("Rcvd: %d '%s'", responseCode, responseLine.Data());
-
- if (responseCode >= ::ResponseCode::UnsolicitedInformational) {
- // These are unsolicited broadcasts. We intercept these and process
- // them ourselves
- HandleBroadcast(responseCode, responseLine);
- } else {
- // Everything else is considered to be part of the command response.
- if (mCommands.size() > 0) {
- VolumeCommand* cmd = mCommands.front();
- cmd->HandleResponse(responseCode, responseLine);
- if (responseCode >= ::ResponseCode::CommandOkay) {
- // That's a terminating response. We can remove the command.
- mCommands.pop();
- mCommandPending = false;
- // Start the next command, if there is one.
- WriteCommandData();
- }
- } else {
- ERR("Response with no command");
- }
- }
-}
-
-void
-VolumeManager::OnFileCanWriteWithoutBlocking(int aFd)
-{
- MOZ_ASSERT(aFd == mSocket.get());
- WriteCommandData();
-}
-
-void
-VolumeManager::HandleBroadcast(int aResponseCode, nsCString& aResponseLine)
-{
- // Format of the line is something like:
- //
- // Volume sdcard /mnt/sdcard state changed from 7 (Shared-Unmounted) to 1 (Idle-Unmounted)
- //
- // So we parse out the volume name and the state after the string " to "
- nsCWhitespaceTokenizer tokenizer(aResponseLine);
- tokenizer.nextToken(); // The word "Volume"
- nsDependentCSubstring volName(tokenizer.nextToken());
-
- RefPtr<Volume> vol = FindVolumeByName(volName);
- if (!vol) {
- return;
- }
- vol->HandleVoldResponse(aResponseCode, tokenizer);
-}
-
-void
-VolumeManager::Restart()
-{
- mReadWatcher.StopWatchingFileDescriptor();
- mWriteWatcher.StopWatchingFileDescriptor();
-
- while (!mCommands.empty()) {
- mCommands.pop();
- }
- mCommandPending = false;
- mSocket.dispose();
- Start();
-}
-
-//static
-void
-VolumeManager::Start()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
-
- if (!sVolumeManager) {
- return;
- }
- SetState(STARTING);
- if (!sVolumeManager->OpenSocket()) {
- // Socket open failed, try again in a second.
- MessageLoopForIO::current()->
- PostDelayedTask(NewRunnableFunction(VolumeManager::Start),
- 1000);
- }
-}
-
-void
-VolumeManager::OnError()
-{
- Restart();
-}
-
-/***************************************************************************/
-
-static void
-InitVolumeManagerIOThread()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
- MOZ_ASSERT(!sVolumeManager);
-
- sVolumeManager = new VolumeManager();
- VolumeManager::Start();
-
- InitVolumeServiceTestIOThread();
-}
-
-static void
-ShutdownVolumeManagerIOThread()
-{
- MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
-
- sVolumeManager = nullptr;
-}
-
-/**************************************************************************
-*
-* Public API
-*
-* Since the VolumeManager runs in IO Thread context, we need to switch
-* to IOThread context before we can do anything.
-*
-**************************************************************************/
-
-void
-InitVolumeManager()
-{
- XRE_GetIOMessageLoop()->PostTask(
- NewRunnableFunction(InitVolumeManagerIOThread));
-}
-
-void
-ShutdownVolumeManager()
-{
- ShutdownVolumeServiceTest();
-
- XRE_GetIOMessageLoop()->PostTask(
- NewRunnableFunction(ShutdownVolumeManagerIOThread));
-}
-
-} // system
-} // mozilla