深入解析vold、MountService(二)(and5.1)

来源:互联网 发布:ppt 高级教程 知乎 编辑:程序博客网 时间:2024/05/04 09:03

在我的博客里,已经对vold和MountService分析过了。http://blog.csdn.net/kc58236582/article/details/46122979

这里在进一步分析。重复的就不再分析了:

main函数里面主要创建了NetlinkManager、VolumeManager、CommandListener这都在另一个博客中分析过了,这里我们主要分析下process_config函数:

int main() {    VolumeManager *vm;    CommandListener *cl;    NetlinkManager *nm;    SLOGI("Vold 2.1 (the revenge) firing up");    mkdir("/dev/block/vold", 0755);    /* For when cryptfs checks and mounts an encrypted filesystem */    klog_set_level(6);    /* Create our singleton managers */    if (!(vm = VolumeManager::Instance())) {        SLOGE("Unable to create VolumeManager");        exit(1);    };    if (!(nm = NetlinkManager::Instance())) {        SLOGE("Unable to create NetlinkManager");        exit(1);    };    cl = new CommandListener();    vm->setBroadcaster((SocketListener *) cl);    nm->setBroadcaster((SocketListener *) cl);    if (vm->start()) {        SLOGE("Unable to start VolumeManager (%s)", strerror(errno));        exit(1);    }    if (process_config(vm)) {        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));    }    if (nm->start()) {        SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));        exit(1);    }    coldboot("/sys/block");//    coldboot("/sys/class/switch");    /*     * Now that we're up, we can respond to commands     */    if (cl->startListener()) {        SLOGE("Unable to start CommandListener (%s)", strerror(errno));        exit(1);    }    // Eventually we'll become the monitoring thread    while(1) {        sleep(1000);    }    SLOGI("Vold exiting");    exit(0);}

process_config函数主要对fstab文件进行解析,然后创建DirectVolume对象,加入VolumManager

static int process_config(VolumeManager *vm){    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];    char propbuf[PROPERTY_VALUE_MAX];    int i;    int ret = -1;    int flags;    property_get("ro.hardware", propbuf, "");    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);    fstab = fs_mgr_read_fstab(fstab_filename);    if (!fstab) {        SLOGE("failed to open %s\n", fstab_filename);        return -1;    }    /* Loop through entries looking for ones that vold manages */    for (i = 0; i < fstab->num_entries; i++) {        if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {//看下面的文件主要对<span style="background-color: rgb(255, 255, 51);">voldmanaged进行解析</span>            DirectVolume *dv = NULL;            flags = 0;            /* Set any flags that might be set for this volume */            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {                flags |= VOL_NONREMOVABLE;            }            if (fs_mgr_is_encryptable(&fstab->recs[i])) {                flags |= VOL_ENCRYPTABLE;            }            /* Only set this flag if there is not an emulated sd card */            if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&                !strcmp(fstab->recs[i].fs_type, "vfat")) {                flags |= VOL_PROVIDES_ASEC;            }            dv = new DirectVolume(vm, &(fstab->recs[i]), flags);            if (dv->addPath(fstab->recs[i].blk_device)) {//将fstab文件中设备的地址add到DirectVolume中                SLOGE("Failed to add devpath %s to volume %s",                      fstab->recs[i].blk_device, fstab->recs[i].label);                goto out_fail;            }            vm->addVolume(dv);//加入到VolumManager中        }    }    ret = 0;out_fail:    return ret;}

fstab文件如下,fstab文件的介绍在我的另一篇博客中http://blog.csdn.net/kc58236582/article/details/47053049


/dev/block/platform/comip-mmc.1/by-name/system                    /system          ext4    ro,barrier=1                                                    wait
/dev/block/platform/comip-mmc.1/by-name/cache                     /cache           ext4    noatime,nosuid,nodev,barrier=1,data=ordered                     wait,check
/dev/block/platform/comip-mmc.1/by-name/userdata                  /data            ext4    noatime,nosuid,nodev,barrier=1,data=ordered,noauto_da_alloc     wait,check,encryptable=footer
#/dev/block/platform/comip-mmc.1/by-name/amt                      /amt             ext4    rw                                                              wait
/devices/platform/comip-mmc.0/mmc_host/mmc1                       auto             vfat    defaults                                                       voldmanaged=sdcard1:auto,noemulatedsd
/devices/platform/comip-hcd/usb1                                  /mnt/media_rw/usbotg      vfat    defaults       voldmanaged=usbotg:auto,noemulatedsd
/dev/block/mmcblk1p1                                              /sdcard                vfat  defaults  recoveryonly
/dev/block/platform/comip-mmc.1/by-name/kernel                                /kernel          emmc        defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk                   /boot                  emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_recovery          /recovery        emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_amt1              /ramdisk_amt1    emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/ramdisk_amt3              /ramdisk_amt3    emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/kernel_recovery           /kernel_recovery emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/logo                      /logo            emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/misc                      /misc            emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/fota                      /fota            emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/modemarm                  /modemarm        emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/modemdsp                  /modemdsp        emmc    defaults    defaults
/dev/block/mmcblk0boot0                                           /uboot           emmc    defaults    defaults
/dev/block/platform/comip-mmc.1/by-name/lcboot                          /lcboot          emmc    defaults    defaults
/dev/block/zram0                                                  none          swap    defaults        zramsize=268435456


我们再来看看DirectVolume的构造函数,会将fstab文件的label初始化mMountpoint 和mFuseMountpoint ,上面文件的两个label分别为sdcard1、usbotg

DirectVolume::DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags) :        Volume(vm, rec, flags) {    mPaths = new PathCollection();    for (int i = 0; i < MAX_PARTITIONS; i++)        mPartMinors[i] = -1;    mPendingPartCount = 0;    mDiskMajor = -1;    mDiskMinor = -1;    mDiskNumParts = 0;    mIsDecrypted = 0;    if (strcmp(rec->mount_point, "auto") != 0) {        ALOGE("Vold managed volumes must have auto mount point; ignoring %s",              rec->mount_point);    }    char mount[PATH_MAX];    snprintf(mount, PATH_MAX, "%s/%s", Volume::MEDIA_DIR, rec->label);//    mMountpoint = strdup(mount);    snprintf(mount, PATH_MAX, "%s/%s", Volume::FUSE_DIR, rec->label);    mFuseMountpoint = strdup(mount);    setState(Volume::State_NoMedia);}

Volume::MEDIA_DIR Volume::FUSE_DIR的定义如下:

/* * Media directory - stuff that only media_rw user can see */const char *Volume::MEDIA_DIR           = "/mnt/media_rw";/* * Fuse directory - location where fuse wrapped filesystems go */const char *Volume::FUSE_DIR           = "/storage";

这样fstab文件有两项,所以我们有两个DirectVolume对象:


当有设备插入的时候,内核会通知我们,调用onEvent函数:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {    VolumeManager *vm = VolumeManager::Instance();    const char *subsys = evt->getSubsystem();    if (!subsys) {        SLOGW("No subsystem found in netlink event");        return;    }    if (!strcmp(subsys, "block")) {        vm->handleBlockEvent(evt);    }}

调用VolumeManager::handleBlockEvent函数

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {    const char *devpath = evt->findParam("DEVPATH");//获取设备的地址    /* Lookup a volume to handle this device */    VolumeCollection::iterator it;    bool hit = false;    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {//遍历所有的volume        if (!(*it)->handleBlockEvent(evt)) {//调用每个volume的handleBlockEvent#ifdef NETLINK_DEBUG            SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());#endif            hit = true;            break;        }    }    if (!hit) {#ifdef NETLINK_DEBUG        SLOGW("No volumes handled block event for '%s'", devpath);#endif    }}

DirectVolume::handleBlockEvent函数分析:

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {    const char *dp = evt->findParam("DEVPATH");//获取设备地址    PathCollection::iterator  it;    for (it = mPaths->begin(); it != mPaths->end(); ++it) {        if ((*it)->match(dp)) {//如果和mpath中匹配            /* We can handle this disk */            int action = evt->getAction();            const char *devtype = evt->findParam("DEVTYPE");            if (action == NetlinkEvent::NlActionAdd) {                int major = atoi(evt->findParam("MAJOR"));                int minor = atoi(evt->findParam("MINOR"));                char nodepath[255];                snprintf(nodepath,                         sizeof(nodepath), "/dev/block/vold/%d:%d",                         major, minor);                if (createDeviceNode(nodepath, major, minor)) {                    SLOGE("Error making device node '%s' (%s)", nodepath,                                                               strerror(errno));                }                if (!strcmp(devtype, "disk")) {                    handleDiskAdded(dp, evt);                } else {                    handlePartitionAdded(dp, evt);                }                /* Send notification iff disk is ready (ie all partitions found) */                if (getState() == Volume::State_Idle) {                    char msg[255];                    snprintf(msg, sizeof(msg),                             "Volume %s %s disk inserted (%d:%d)", getLabel(),                             getFuseMountpoint(), mDiskMajor, mDiskMinor);                    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,//发到MountService,VolumeDiskInserted                                                         msg, false);                }            } else if (action == NetlinkEvent::NlActionRemove) {                if (!strcmp(devtype, "disk")) {                    handleDiskRemoved(dp, evt);                } else {                    handlePartitionRemoved(dp, evt);                }            } else if (action == NetlinkEvent::NlActionChange) {                if (!strcmp(devtype, "disk")) {                    handleDiskChanged(dp, evt);                } else {                    handlePartitionChanged(dp, evt);                }            } else {                    SLOGW("Ignoring non add/remove/change event");            }            return 0;        }    }    errno = ENODEV;    return -1;}


因此我们下面首先来介绍下MountService,然后再来分析vold发来的VolumeDiskInserted状态是怎么处理的.

MountService在构造函数里面会去调用readStorageListLocked函数:

    public MountService(Context context) {        sSelf = this;        mContext = context;        synchronized (mVolumesLock) {            readStorageListLocked();        }

下面我们详细分析下readStorageListLocked函数:

    private void readStorageListLocked() {        mVolumes.clear();        mVolumeStates.clear();        Resources resources = mContext.getResources();        int id = com.android.internal.R.xml.storage_list;        XmlResourceParser parser = resources.getXml(id);        AttributeSet attrs = Xml.asAttributeSet(parser);        try {            XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);            while (true) {                XmlUtils.nextElement(parser);                String element = parser.getName();                if (element == null) break;                if (TAG_STORAGE.equals(element)) {                    TypedArray a = resources.obtainAttributes(attrs,                            com.android.internal.R.styleable.Storage);                    String path = a.getString(                            com.android.internal.R.styleable.Storage_mountPoint);                    int descriptionId = a.getResourceId(                            com.android.internal.R.styleable.Storage_storageDescription, -1);                    CharSequence description = a.getText(                            com.android.internal.R.styleable.Storage_storageDescription);                    boolean primary = a.getBoolean(                            com.android.internal.R.styleable.Storage_primary, false);                    boolean removable = a.getBoolean(                            com.android.internal.R.styleable.Storage_removable, false);                    boolean emulated = a.getBoolean(                            com.android.internal.R.styleable.Storage_emulated, false);                    int mtpReserve = a.getInt(                            com.android.internal.R.styleable.Storage_mtpReserve, 0);                    boolean allowMassStorage = a.getBoolean(                            com.android.internal.R.styleable.Storage_allowMassStorage, false);                    // resource parser does not support longs, so XML value is in megabytes                    long maxFileSize = a.getInt(                            com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;                    Slog.d(TAG, "got storage path: " + path + " description: " + description +                            " primary: " + primary + " removable: " + removable +                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve +                            " allowMassStorage: " + allowMassStorage +                            " maxFileSize: " + maxFileSize);    //上面都是对xml文件的解析过程,我们看看下面的xml文件                    if (emulated) {//如果emulated为true                        // For devices with emulated storage, we create separate                        // volumes for each known user.                        mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,                                true, mtpReserve, false, maxFileSize, null);                        final UserManagerService userManager = UserManagerService.getInstance();                        for (UserInfo user : userManager.getUsers(false)) {                            createEmulatedVolumeForUserLocked(user.getUserHandle());                        }                    } else {                        if (path == null || description == null) {                            Slog.e(TAG, "Missing storage path or description in readStorageList");                        } else {                            final StorageVolume volume = new StorageVolume(new File(path),                                    descriptionId, primary, removable, emulated, mtpReserve,                                    allowMassStorage, maxFileSize, null);                            addVolumeLocked(volume);//调用addVolumeLocked函数                            // Until we hear otherwise, treat as unmounted                            mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);//mVolumeStates会把所有的path的状态保存下来,比较重要。getVolumState都是从这个成员变量中取                            volume.setState(Environment.MEDIA_UNMOUNTED);//状态置为Environment.MEDIA_UNMOUNTED                        }                    }                    a.recycle();                }            }        } catch (XmlPullParserException e) {            throw new RuntimeException(e);        } catch (IOException e) {            throw new RuntimeException(e);        } finally {            // Compute storage ID for each physical volume; emulated storage is            // always 0 when defined.            int index = isExternalStorageEmulated() ? 1 : 0;            for (StorageVolume volume : mVolumes) {                if (!volume.isEmulated()) {                    volume.setStorageId(index++);                }            }            parser.close();        }    }

storage_list.xml文件如下:

<StorageList xmlns:android="http://schemas.android.com/apk/res/android">    <!-- removable is not set in nosdcard product -->    <!--use for default -->    <storage android:mountPoint="/storage/sdcard0"                android:storageDescription="@string/storage_internal"                android:primary="true"                android:emulated="true"                android:allowMassStorage="false"                android:removable="false"                />    <storage android:mountPoint="/storage/sdcard1"                android:storageDescription="@string/storage_sd_card"                android:primary="false"                android:removable="true"                android:emulated="false"                android:allowMassStorage="true"                android:mtpReserve="0"                />    <storage android:mountPoint="/storage/usbotg"                android:storageDescription="@string/storage_usb_otg"                android:primary="false"                android:removable="true"                android:emulated="false"                android:allowMassStorage="false"                />
createEmulatedVolumeForUserLocked函数会针对多用户,详细不分析,最后的地址/storage/emulated/0,其中0代表userId

    private void createEmulatedVolumeForUserLocked(UserHandle user) {        if (mEmulatedTemplate == null) {            throw new IllegalStateException("Missing emulated volume multi-user template");        }        final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());        final File path = userEnv.getExternalStorageDirectory();        final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);        volume.setStorageId(0);        addVolumeLocked(volume);//调用addVolumeLocked        if (mSystemReady) {            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);//状态置为Environment.MEDIA_MOUNTED,因为这是内置sd卡        } else {            // Place stub status for early callers to find            mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);            volume.setState(Environment.MEDIA_MOUNTED);        }    }

addVolumeLocked函数如下:将volume加到mVolumes,并且将volume的path加到mVolumesByPath

    private void addVolumeLocked(StorageVolume volume) {        Slog.d(TAG, "addVolumeLocked() " + volume);        mVolumes.add(volume);        final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);        if (existing != null) {            throw new IllegalStateException(                    "Volume at " + volume.getPath() + " already exists: " + existing);        }    }



回到前面vold收到内核有检测到外部存储信息,vold往MountService发送VolumeDiskInserted状态,MountService会在onEvent中收到消息:

public boolean onEvent(int code, String raw, String[] cooked) {.....            else if ((code == VoldResponseCode.VolumeDiskInserted) ||                   (code == VoldResponseCode.VolumeDiskRemoved) ||                   (code == VoldResponseCode.VolumeBadRemoval)) {            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)            String action = null;            final String label = cooked[2];            final String path = cooked[3];            int major = -1;            int minor = -1;            try {                String devComp = cooked[6].substring(1, cooked[6].length() -1);                String[] devTok = devComp.split(":");                major = Integer.parseInt(devTok[0]);                minor = Integer.parseInt(devTok[1]);            } catch (Exception ex) {                Slog.e(TAG, "Failed to parse major/minor", ex);            }            final StorageVolume volume;            final String state;            synchronized (mVolumesLock) {                volume = mVolumesByPath.get(path);                state = mVolumeStates.get(path);            }            if (code == VoldResponseCode.VolumeDiskInserted) {//如果是VolumeDiskInserted,直接开个线程调用doMountVolume函数                new Thread("MountService#VolumeDiskInserted") {                    @Override                    public void run() {                        try {                            int rc;                            if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {                                Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));                            }                        } catch (Exception ex) {                            Slog.w(TAG, "Failed to mount media on insertion", ex);                        }                    }                }.start();            } ........

doMountVolume函数如下:

    private int doMountVolume(String path) {        int rc = StorageResultCode.OperationSucceeded;        final StorageVolume volume;        synchronized (mVolumesLock) {            volume = mVolumesByPath.get(path);        }        if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {            Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");            return StorageResultCode.OperationFailedInternalError;        }        if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);        try {            mConnector.execute("volume", "mount", path);//mConnector是和vold通信的就不分析了        } catch (NativeDaemonConnectorException e) {//下面都是一些异常            /*             * Mount failed for some reason             */            String action = null;            int code = e.getCode();            if (code == VoldResponseCode.OpFailedNoMedia) {                /*                 * Attempt to mount but no media inserted                 */                rc = StorageResultCode.OperationFailedNoMedia;            } else if (code == VoldResponseCode.OpFailedMediaBlank) {                if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");                /*                 * Media is blank or does not contain a supported filesystem                 */                updatePublicVolumeState(volume, Environment.MEDIA_NOFS);                action = Intent.ACTION_MEDIA_NOFS;                rc = StorageResultCode.OperationFailedMediaBlank;            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");                /*                 * Volume consistency check failed                 */                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);                action = Intent.ACTION_MEDIA_UNMOUNTABLE;                rc = StorageResultCode.OperationFailedMediaCorrupt;            } else {                rc = StorageResultCode.OperationFailedInternalError;            }            /*             * Send broadcast intent (if required for the failure)             */            if (action != null) {                sendStorageIntent(action, volume, UserHandle.ALL);            }        }        return rc;    }


这样又到vold了,首先接受MountService发来的mount信息:

vold收MountService消息,是在CommandListener::VolumeCmd::runCommand函数中

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,                                           int argc, char **argv) {    dumpArgs(argc, argv, -1);    if (argc < 2) {        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);        return 0;    }    VolumeManager *vm = VolumeManager::Instance();    int rc = 0;.......    else if (!strcmp(argv[1], "mount")) {        if (argc != 3) {            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);            return 0;        }        rc = vm->mountVolume(argv[2]);//直接调用volumeManager的mountVolume函数    }    ...........

VolumeManager::mountVolume函数:

int VolumeManager::mountVolume(const char *label) {    Volume *v = lookupVolume(label);//现在VolumManager中查找volume    if (!v) {        errno = ENOENT;        return -1;    }    return v->mountVol();//调用DirectVolume的mountVol函数}

根据上面发下来的地址和getFuseMountpoint来比较,就是以 "/storage"开始的地址

Volume *VolumeManager::lookupVolume(const char *label) {    VolumeCollection::iterator i;    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {        if (label[0] == '/') {            if (!strcmp(label, (*i)->getFuseMountpoint()))                return (*i);        } else {            if (!strcmp(label, (*i)->getLabel()))                return (*i);        }    }    return NULL;}


具体的挂载函数不分析了,注意每次状态改变setState的时候都会给MountService发送消息。


如果vold收到内核的VolumeDiskRemoved消息:

void DirectVolume::handleDiskRemoved(const char * /*devpath*/,                                     NetlinkEvent *evt) {    int major = atoi(evt->findParam("MAJOR"));    int minor = atoi(evt->findParam("MINOR"));    char msg[255];    bool enabled;    if (mVm->shareEnabled(getLabel(), "ums", &enabled) == 0 && enabled) {        mVm->unshareVolume(getLabel(), "ums");    }    SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);    snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",             getLabel(), getFuseMountpoint(), major, minor);    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,//发送到MountService                                             msg, false);    setState(Volume::State_NoMedia);}


看看MountService的onEvent函数,更新下状态

            } else if (code == VoldResponseCode.VolumeDiskRemoved) {                /*                 * This event gets trumped if we're already in BAD_REMOVAL state                 */                if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {                    return true;                }                /* Send the media unmounted event first */                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");                updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);                action = Intent.ACTION_MEDIA_REMOVED;            }


Mountservice卸载通过接口unmountVolume

    public void unmountVolume(String path, boolean force, boolean removeEncryption) {        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);        waitForReady();        String volState = getVolumeState(path);        if (DEBUG_UNMOUNT) {            Slog.i(TAG, "Unmounting " + path                    + " force = " + force                    + " removeEncryption = " + removeEncryption);        }        if (Environment.MEDIA_UNMOUNTED.equals(volState) ||                Environment.MEDIA_REMOVED.equals(volState) ||                Environment.MEDIA_SHARED.equals(volState) ||                Environment.MEDIA_UNMOUNTABLE.equals(volState)) {            // Media already unmounted or cannot be unmounted.            // TODO return valid return code when adding observer call back.            return;        }        UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);        mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));//是通过发送消息    }


最后再去调用UnmountCallBack 的handleFinished去下发unmount命令

    class UnmountCallBack {        final String path;        final boolean force;        final boolean removeEncryption;        int retries;        UnmountCallBack(String path, boolean force, boolean removeEncryption) {            retries = 0;            this.path = path;            this.force = force;            this.removeEncryption = removeEncryption;        }        void handleFinished() {            if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);            doUnmountVolume(path, true, removeEncryption);        }    }


下面看看vold对unmount的处理:

int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {    Volume *v = lookupVolume(label);    if (!v) {        errno = ENOENT;        return -1;    }    if (v->getState() == Volume::State_NoMedia) {        errno = ENODEV;        return -1;    }    if (v->getState() != Volume::State_Mounted) {        SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",             v->getState());        errno = EBUSY;        return UNMOUNT_NOT_MOUNTED_ERR;    }    cleanupAsec(v, force);    return v->unmountVol(force, revert);//MountService发送的参数force都是true,就是强制卸载的}

Volume::unmountVol函数

int Volume::unmountVol(bool force, bool revert) {    int i, rc;    int flags = getFlags();    bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;    if (getState() != Volume::State_Mounted) {        SLOGE("Volume %s unmount request when not mounted", getLabel());        errno = EINVAL;        return UNMOUNT_NOT_MOUNTED_ERR;    }    setState(Volume::State_Unmounting);//先将状态置为Volume::State_Unmounting    usleep(1000 * 1000); // Give the framework some time to react    char service[64];    snprintf(service, 64, "fuse_%s", getLabel());    property_set("ctl.stop", service);    /* Give it a chance to stop.  I wish we had a synchronous way to determine this... */    sleep(1);    // TODO: determine failure mode if FUSE times out    if (providesAsec && doUnmount(Volume::SEC_ASECDIR_EXT, force) != 0) {        SLOGE("Failed to unmount secure area on %s (%s)", getMountpoint(), strerror(errno));        goto out_mounted;    }    /* Now that the fuse daemon is dead, unmount it */    if (doUnmount(getFuseMountpoint(), force) != 0) {        SLOGE("Failed to unmount %s (%s)", getFuseMountpoint(), strerror(errno));        goto fail_remount_secure;    }    /* Unmount the real sd card */    if (doUnmount(getMountpoint(), force) != 0) {        SLOGE("Failed to unmount %s (%s)", getMountpoint(), strerror(errno));        goto fail_remount_secure;    }    SLOGI("%s unmounted successfully", getMountpoint());    /* If this is an encrypted volume, and we've been asked to undo     * the crypto mapping, then revert the dm-crypt mapping, and revert     * the device info to the original values.     */    if (revert && isDecrypted()) {        cryptfs_revert_volume(getLabel());        revertDeviceInfo();        SLOGI("Encrypted volume %s reverted successfully", getMountpoint());    }    setUuid(NULL);    setUserLabel(NULL);    setState(Volume::State_Idle);//最后成功再讲状态置为Volume::State_Idle    mCurrentlyMountedKdev = -1;    return 0;fail_remount_secure:    if (providesAsec && mountAsecExternal() != 0) {        SLOGE("Failed to remount secure area (%s)", strerror(errno));        goto out_nomedia;    }out_mounted:    setState(Volume::State_Mounted);    return -1;out_nomedia:    setState(Volume::State_NoMedia);    return -1;}

doUnmount函数:

int Volume::doUnmount(const char *path, bool force) {    int retries = 10;    if (mDebug) {        SLOGD("Unmounting {%s}, force = %d", path, force);    }    while (retries--) {//执行10次        if (!umount(path) || errno == EINVAL || errno == ENOENT) {            SLOGI("%s sucessfully unmounted", path);//如果直接成功退出了            return 0;        }        int action = 0;//如果有进程正在使用该设备就不能卸载。        if (force) {//等待到retries == 1或者2的时候            if (retries == 1) {                action = 2; // SIGKILL            } else if (retries == 2) {                action = 1; // SIGHUP            }        }        SLOGW("Failed to unmount %s (%s, retries %d, action %d)",                path, strerror(errno), retries, action);        Process::killProcessesWithOpenFiles(path, action);//杀了使用这个路径的进程,杀了之后就可以卸载了        usleep(1000*1000);    }    errno = EBUSY;    SLOGE("Giving up on unmount %s (%s)", path, strerror(errno));    return -1;}

killProcessesWithOpenFiles函数,先使用SIGTERM杀,如果还杀不掉使用SIGKILL杀,SIGKILL可以强制杀

void Process::killProcessesWithOpenFiles(const char *path, int action) {    DIR*    dir;    struct dirent* de;    if (!(dir = opendir("/proc"))) {        SLOGE("opendir failed (%s)", strerror(errno));        return;    }    while ((de = readdir(dir))) {        int killed = 0;        int pid = getPid(de->d_name);        char name[PATH_MAX];        if (pid == -1)            continue;        getProcessName(pid, name, sizeof(name));        char openfile[PATH_MAX];        if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) {            SLOGE("Process %s (%d) has open file %s", name, pid, openfile);        } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) {            SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile);        } else if (checkSymLink(pid, path, "cwd")) {            SLOGE("Process %s (%d) has cwd within %s", name, pid, path);        } else if (checkSymLink(pid, path, "root")) {            SLOGE("Process %s (%d) has chroot within %s", name, pid, path);        } else if (checkSymLink(pid, path, "exe")) {            SLOGE("Process %s (%d) has executable path within %s", name, pid, path);        } else {            continue;        }        if (action == 1) {            SLOGW("Sending SIGHUP to process %d", pid);            kill(pid, SIGTERM);        } else if (action == 2) {            SLOGE("Sending SIGKILL to process %d", pid);            kill(pid, SIGKILL);        }    }    closedir(dir);}

MountService收到后调用onEvent函数:

        if (code == VoldResponseCode.VolumeStateChange) {            /*             * One of the volumes we're managing has changed state.             * Format: "NNN Volume <label> <path> state changed             * from <old_#> (<old_str>) to <new_#> (<new_str>)"             */            notifyVolumeStateChange(                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),                            Integer.parseInt(cooked[10]));        } 

notifyVolumeStateChange函数中,收到VolumeState.Idle后对它进行判断后,将状态置成Environment.MEDIA_UNMOUNTED,并且最后会发送广播给应用。

else if (newState == VolumeState.Idle) {            /*             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or             * if we're in the process of enabling UMS             */            if (!state.equals(                    Environment.MEDIA_BAD_REMOVAL) && !state.equals(                            Environment.MEDIA_NOFS) && !state.equals(                                    Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);                action = Intent.ACTION_MEDIA_UNMOUNTED;            }        } 

还有说下updatePublicVolumeState这个函数,想setting对volume的检测,就不是通过广播的,而是通过在MountService中注册回调。等状态改变后会调用updatePublicVolumeState来改变volume的状态,最后再去遍历通知回调。比如在Setting收到后,可以调用MountService的getVolumeState查询最新的状态。

    private void updatePublicVolumeState(StorageVolume volume, String state) {        final String path = volume.getPath();        final String oldState;        synchronized (mVolumesLock) {            oldState = mVolumeStates.put(path, state);            volume.setState(state);//设置下volume的状态        }        if (state.equals(oldState)) {            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",                    state, state, path));            return;        }        Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");        // Tell PackageManager about changes to primary volume state, but only        // when not emulated.        if (volume.isPrimary() && !volume.isEmulated()) {            if (Environment.MEDIA_UNMOUNTED.equals(state)) {                mPms.updateExternalMediaStatus(false, false);                /*                 * Some OBBs might have been unmounted when this volume was                 * unmounted, so send a message to the handler to let it know to                 * remove those from the list of mounted OBBS.                 */                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(                        OBB_FLUSH_MOUNT_STATE, path));            } else if (Environment.MEDIA_MOUNTED.equals(state)) {                mPms.updateExternalMediaStatus(true, false);            }        }        synchronized (mListeners) {            for (int i = mListeners.size() -1; i >= 0; i--) {//通知注册到MountService的回调。通知哪个path的volume状态改变了                MountServiceBinderListener bl = mListeners.get(i);                try {                    bl.mListener.onStorageStateChanged(path, oldState, state);                } catch (RemoteException rex) {                    Slog.e(TAG, "Listener dead");                    mListeners.remove(i);                } catch (Exception ex) {                    Slog.e(TAG, "Listener failed", ex);                }            }        }    }






2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 脸上皮肤毛孔粗大有黑头怎么办 嗓子长了个囊肿怎么办 食管胃粘膜异位怎么办 狗狗肿瘤破了怎么办 婴儿胃食道反流怎么办 小儿胃食道反流怎么办 放疗后咳嗽痰多怎么办 胃息肉是恶性的怎么办 老是胃疼,胃胀怎么办 吃完饭之后胃胀怎么办 胃痛胃胀怎么办能缓解 胃胀不消化怎么办快速解决 便秘肛裂了好痛怎么办 胃消化慢还便秘怎么办 11个月婴儿便秘怎么办 80多岁老人便秘怎么办 狗狗便秘拉不出来怎么办 2个月幼犬便秘怎么办 狗狗便秘怎么办吃什么 痔疮又痛又痒怎么办 痔疮肉球特别痒怎么办 长了个小痔疮怎么办 产后4天没大便怎么办 7个月孕妇痔疮怎么办 运动完恶心想吐怎么办 跑步后恶心想吐怎么办 肠子不蠕动严重便秘怎么办 怀孕八个月严重便秘怎么办 怀孕七个月便秘严重怎么办 怀孕两个月便秘严重怎么办 3岁宝宝上火便秘怎么办 7个月的宝宝贫血怎么办 9个月婴儿贫血怎么办 肛裂大便有血怎么办 生完宝宝肛门痛怎么办 肛周脓肿出血了怎么办 胃胀怎么办简单的办法 吃多了胃胀难受怎么办 胃窦炎胆汁反流怎么办 怀孕总胆汁酸高怎么办 胃里胆汁反流怎么办