Android Open Source - Mountie Block Device Observer






From Project

Back to project page Mountie.

License

The source code is released under:

GNU General Public License

If you think the Android project Mountie listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Mountie, a tool for mounting external storage on Android
 * Copyright (C) 2014 Andrew Comminos <andrew@morlunk.com>
 *// ww w .jav  a2s .  c  om
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.morlunk.mountie.fs;

import android.os.FileObserver;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.morlunk.mountie.Constants;
import com.morlunk.mountie.command.BlkidCommand;
import com.stericson.RootTools.execution.Command;
import com.stericson.RootTools.execution.Shell;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Monitors /dev/block for devices using inotify, and uses the system implementation of blkid to
 * read device information. We would use inotify events on /proc/partitions, but it's unreliable.
 * Stores a database of block devices and their mounts.
 * Created by andrew on 14/09/14.
 */
public class BlockDeviceObserver extends FileObserver {
    private static final Pattern DEV_PATTERN = Pattern.compile("(sd[a-z]+)(\\d+)?");
    /** A mapping of device identifiers (i.e. sda) to volumes. */
    private Map<String, Volume> mVolumes;
    private PartitionListener mListener;
    private Handler mHandler;
    private Shell mRootShell;

    /**
     * Creates a new block device observer.
     * Does not start observing until {@link #startWatching()} is called.
     * @param rootShell The shell to execute mount commands in.
     * @param listener A listener to receive block device events.
     *                 Calls are received on the main thread.
     */
    public BlockDeviceObserver(Shell rootShell, PartitionListener listener) {
        super("/dev/block/", FileObserver.CREATE | FileObserver.DELETE);
        mVolumes = new HashMap<String, Volume>();
        mListener = listener;
        mHandler = new Handler(Looper.getMainLooper());
        mRootShell = rootShell;
        detectDevices();
    }

    /**
     * Detects devices manually.
     * To be used if we missed block device registrations with inotify.
     */
    public void detectDevices() {
        Command blkidCommand = new BlkidCommand(0, new BlkidCommand.Listener() {
            @Override
            public void onBlkidResult(List<Partition> partitions) {
                for (Partition partition : partitions) {
                    String volumeName = partition.getVolumeName();
                    Volume volume = mVolumes.get(volumeName);
                    if (volume == null) {
                        volume = new Volume(volumeName);
                        mVolumes.put(volumeName, volume);
                        mListener.onVolumeAdded(volume);
                    }
                    volume.addPartition(partition.getLogicalId(), partition);
                    mListener.onPartitionAdded(volume, partition);
                }
            }

            @Override
            public void onBlkidFailure() {
                Log.e(Constants.TAG, "Failed to call blkid!");
            }
        });
        try {
            mRootShell.add(blkidCommand);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onEvent(int event, final String relativePath) {
        Matcher matcher = DEV_PATTERN.matcher(relativePath);
        if (!matcher.matches()) {
            return;
        }

        String volumeName = matcher.group(1);
        boolean logical = matcher.group(2) != null;

        // FIXME? We assume we receive volumes before logical partition block devices.
        if (event == FileObserver.CREATE) {
            if (logical) {
                final int logicalId = Integer.valueOf(matcher.group(2));
                final Volume volume = mVolumes.get(volumeName);
                if (volume != null) {
                    // Run blkid to determine filesystem, label, and UUID
                    Command blkidCommand = new BlkidCommand(0, relativePath, new BlkidCommand.Listener() {
                        @Override
                        public void onBlkidResult(List<Partition> partitions) {
                            if (partitions.size() == 0) {
                                return; // Couldn't find device in blkid
                            }

                            final Partition partition = partitions.get(0);
                            volume.addPartition(logicalId, partition);
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mListener.onPartitionAdded(volume, partition);
                                }
                            });
                        }

                        @Override
                        public void onBlkidFailure() {
                            Log.e(Constants.TAG, "Failed to call blkid for " +
                                    "discovered partition " + relativePath);
                        }
                    });

                    try {
                        mRootShell.add(blkidCommand);
                    } catch (IOException e) {
                        Log.e(Constants.TAG, "Failed to call blkid for " +
                                "discovered partition " + relativePath);
                        e.printStackTrace();
                    }
                } else {
                    Log.e(Constants.TAG, "No volume found for partition " + relativePath);
                }
            } else {
                final Volume volume = new Volume(volumeName);
                mVolumes.put(volumeName, volume);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onVolumeAdded(volume);
                    }
                });
            }
        } else if (event == FileObserver.DELETE) {
            if (logical) {
                int logicalId = Integer.valueOf(matcher.group(2));
                final Volume volume = mVolumes.get(volumeName);
                if (volume != null) {
                    final Partition partition = volume.getPartition(logicalId);
                    volume.removePartition(logicalId);
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mListener.onPartitionRemoved(volume, partition);
                        }
                    });
                }
            } else {
                final Volume volume = mVolumes.get(volumeName);
                mVolumes.remove(volumeName);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mListener.onVolumeRemoved(volume);
                    }
                });
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }

    public Collection<Volume> getVolumes() {
        return mVolumes.values();
    }
}




Java Source Code List

com.morlunk.mountie.ApplicationTest.java
com.morlunk.mountie.Automounter.java
com.morlunk.mountie.Constants.java
com.morlunk.mountie.MountieActivity.java
com.morlunk.mountie.MountieBootReceiver.java
com.morlunk.mountie.MountieNotification.java
com.morlunk.mountie.MountieService.java
com.morlunk.mountie.UsbHotplugReceiver.java
com.morlunk.mountie.command.BlkidCommand.java
com.morlunk.mountie.fs.BlockDeviceObserver.java
com.morlunk.mountie.fs.BlockDevice.java
com.morlunk.mountie.fs.MountException.java
com.morlunk.mountie.fs.MountListener.java
com.morlunk.mountie.fs.Mount.java
com.morlunk.mountie.fs.PartitionListener.java
com.morlunk.mountie.fs.Partition.java
com.morlunk.mountie.fs.UnmountListener.java
com.morlunk.mountie.fs.Volume.java
com.morlunk.mountie.jni.Native.java
com.morlunk.mountie.util.Filesystems.java
com.stericson.RootTools.Constants.java
com.stericson.RootTools.RootTools.java
com.stericson.RootTools.containers.Mount.java
com.stericson.RootTools.containers.Permissions.java
com.stericson.RootTools.containers.RootClass.java
com.stericson.RootTools.containers.Symlink.java
com.stericson.RootTools.exceptions.RootDeniedException.java
com.stericson.RootTools.execution.CommandCapture.java
com.stericson.RootTools.execution.Command.java
com.stericson.RootTools.execution.JavaCommandCapture.java
com.stericson.RootTools.execution.Shell.java
com.stericson.RootTools.internal.Installer.java
com.stericson.RootTools.internal.InternalVariables.java
com.stericson.RootTools.internal.Remounter.java
com.stericson.RootTools.internal.RootToolsInternalMethods.java
com.stericson.RootTools.internal.Runner.java