Android External Storage Support: Volume Daemon (vold) Architecture


Introduction.

When we insert an sdcard/usb drive in an Android device, The system automatically mounts them for use. The vold [short for Volume Daemon] is the service responsible for detecting and mounting/unmounting of all the external or extended storage media in Android like usb, sdcard, cdrom.

Its main function involves in helping Android support  hot pluggable storage devices.
In this tutorial we will understand vold and associated components along with their code walkthrough.



The vold process receives external storage device connection or disconnection messages from the kernel to manage and control the external storage devices on the Android platform.
They include sdcard plugging, mounting, uninstalling, and formatting.

When the external device changes, the kernel sends messages in uEvent format through Netlink.
For user space programs, Netlink is a special socket based communication mechanism  which is asynchronous and used for bidirectional data transfer between the kernel and the user application.

The user socket application can use the standard socket API to use the powerful functions provided by Netlink(Please visit the wiki page for more details).





What we see above from the diagram is a basic workflow model of how vold works and some of the components involved.

vold is a native program for managing and controlling the control center of the external storage device on the Android platform. It is a background running process. It interacts with the Java layer MountService. vold receives uevent messages from the kernel and forwards them to the upper layer.

The MountService receives messages from vold and can also send control commands to vold. vold has broadly three modules, NetlinkManager, VolumeManager, and CommandListener.

The NetlinkManager module specifically receives the uevent message from the Linux kernel and forwards the message to the VolumeManager.

The VolumeManager module then sends the related information to the MountService through the CommandListener.

The MountService sends the corresponding processing command to the VolumeManager according to the received message, and the VolumeManager receives the command. Then directly operate the external storage device.

The CommandListener module internally encapsulates a Socket for cross-process communication. The client-side MountService of the Java layer communicates with the server-side vold via the Socket.


The required device information is exported by the sysfs file system. For every device the kernel has detected and initialized, a directory with the device name is created. It contains attribute files with device-specific properties. Every time a device is added or removed, the kernel sends a uevent to notify udev of the change.

The udev daemon reads and parses all provided rules from the /etc/udev/rules.d/*.rules files once at start-up and keeps them in memory. If rules files are changed, added, or removed, the daemon receives an event and updates the in-memory representation of the rules.

Every received event is matched against the set of provides rules. The rules can add or change event environment keys, request a specific name for the device node to create, add symlinks pointing to the node, or add programs to run after the device node is created. The driver core uevents are received from a kernel netlink socket.

We will go through most of these in the coming sections below, However it makes great sense to start with the instance where the vold starts.

Source Code Analysis.

Vold startup.

During boot the init.rc starts vold process, Let us see how.


The vold executable is in /system/bin.




The vold is implemented as part of system/vold , Let us start with system/vold/main.cpp.


Let us see some of the important steps as part of the vold startup.
  


1. Check and initialize the selinux file context handle.
  

2. Create the vold device folder.



3. Initialize and start the related classes of VolumeManager, NetlinkManager, CommandListener and CryptCommandListener.





4. Start the VolumeManager and NetlinkManager.


 5. Start the CommandListener and eventually become the monitoring thread.


Remember that VolumeManager and NetlinkManager are singletons.






The process_config function reads the fstab file and initialize the disksources as seen above.


Vold component relationship.

We already understood that vold typically consists of 3 components, NetlinkManager, VolumeManager, and CommandListener.


We shall visit these components in order to understand more on them.


The NetlinkManager receives the uevent messages from the Kernel, resolves it into a NetlinkEvent object, and passes this NetlinkEvent object to the VolumeManager for processing.



We can see from above that the NetlinkManager on starting creates a socket and binds to it and starts the NetlinkHandler associated with the socket.

The job of NetlinkHandler is to check check if the events received from the uevent are for block devices and asj the VolumeManager to handle them.


Let us also quickly have a small glance at the SocketListener::startListener code which is self explanatory.
  








The SocketListener::threadStart starts the Listener above which keeps waiting for an uevent message.

The most important part of SocketListener::runListener() is listening on socketlist and as soon as it gets a message call the onDataAvailable().







We can see how onEvent(evt); gets called by the NetlinkListener::onDataAvailable and implemented by NetlinkHandler::onEvent() as shown above which checks if the message belongs to subsys calls VolumeManager to handle this message and we also complete our loop.


VolumeManager.

The VolumeManager module sends the related information obtained above to the MountService through the CommandListener.
Major steps which involve in the working of VolumeManager are.

1) Constructing VolumeManager object instance
2) Setting up event broadcast monitoring
3) Starting VolumeManager
4) Configuring VolumeManager



Let us get into a code walkthrough and we will understand things more.





We already know that VolumeManager is a singleton. Let us move forward.







ueevent processing.


The NetlinkManager module introduced above has an onEvent function which gets the NetlinkEvents and passes on to the VolumeManager for further processing.

Let us see how that works.





Before we move and see the handling of these NetLinkEvents, Let us try and understand what are these events.


 

The names of these Events are self-explanatory. Let us continue.


 Let us also look at some of the paths and device numbers which are used to add / remove these volumes.



 Let us see how VolumeManager::handleBlockEvent works.
  


We start by extracting the DEVPATH and DEVTYPE from the event message. Only process the message if the DEVTYPE is “disk”.



Find the MAJOR & MINOR numbers for the device driver and create it.

NetlinkEvent::Action::kAdd
NetlinkEvent::Action::kChange
NetlinkEvent::Action::kRemove


Look at the Disk class other functions in detail if you want to learn actually how devices are maintained.



Depending on the type of events we add or remove these disks. Let us also see the Disk class which is actually responsible for creating and managing Disk connections in Disk.cpp above

Once the Disk add/removes/modifies these volume entries, It Broadcasts this to the MountService using the CommandListener which is inherited from FrameworkListener which is again inherited from SocketListener.



CommandListener.

Let us see some more details on CommandListener.








Message processing.

When receiving a message sent by the MountService, FrameworkListener::onDataAvailable is called to handle the command.
The CommandListener parent FrameworkListener overrides the message handling onDataAvailable function.







FrameworkListener::dispatchCommand later after doing all checks and balances calls runCommand function to handle various commands sent by the MountService.


We already saw how FrameworkCommand gets registered along with their handler functions above.
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd());
    registerCmd(new AsecCmd());
    registerCmd(new ObbCmd());
    registerCmd(new StorageCmd());
    registerCmd(new FstrimCmd()); as shown below.


Please check the CommandListener for more details. Logically every command registers a runCommand function which is responsible for the corresponding command implementation example.


Let us see another runCommand dunction for the volume command.


We can get into the details of this function however it is really simple to understand.

Typically most of the functionality is implemented by VolumeBase class we can see some of them below.



We will try and understand more on the details of MountService in the coming tutorials to see how it is created and different scenarios in which it works.


Comments

  1. Now, I understand how SD card is detected.

    ReplyDelete
  2. This is exactly what I'm looking for and I Google anything. I have no idea how to make my Google bring up more answers like yours but I wish it was as easy as Googling it. LOL thank you so much.

    ReplyDelete
  3. DAEMON Tools Pro 8.3.0.0767 Crack
    I am very impressed with your post because this post is very beneficial for me and provide a new knowledge to me

    ReplyDelete

Post a Comment

Popular posts from this blog

Android Audio Tutorial [Part Three] : AudioFlinger Introduction and Initialization

Android Audio Tutorial [Part One] : Introduction