Android vold进程三 MountService介绍

来源:互联网 发布:徐留平一汽改革知乎 编辑:程序博客网 时间:2024/05/17 16:46

一、MountService的创建
  MountService:Android Binder服务端,运行在system_server进程,用于跟Vold进行消息通信,比如MountService向Vold发送挂载SD卡的命令,或者接收到来自Vold的外设热插拔事件。MountService作为Binder服务端,那么相应的Binder客户端便是StorageManager,通过binder IPC与MountService交互。
  MountService是一个系统服务,是在SystemServer中启动的,这里new 了一个MountService,并把service添加到了ServiceManager管理:

./frameworks/base/services/java/com/android/server/SystemServer.javaprivate void startOtherServices() {    ......    try {        /*         * NotificationManagerService is dependant on MountService,         * (for media / usb notifications) so we must start MountService first.         */        //启动MountService服务        mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);        //等价new IMountService.Stub.Proxy(),即获取MountService的proxy对象        mountService = IMountService.Stub.asInterface(                ServiceManager.getService("mount"));    } catch (Throwable e) {        reportWtf("starting Mount Service", e);    }    ......}

  上面mSystemServiceManager.startService(MOUNT_SERVICE_CLASS)主要完成3件事:
1、创建MOUNT_SERVICE_CLASS所指类的Lifecycle对象;
2、将该对象添加SystemServiceManager的 mServices 服务列表;
3、最后调用Lifecycle的onStart()方法;
  下面看Lifecycle的onStart方法:

    public static class Lifecycle extends SystemService {        private MountService mMountService;        private String oldDefaultPath = "";        public Lifecycle(Context context) {            super(context);        }        @Override        public void onStart() {            Slog.d(TAG, "MountService onStart");            sSelf.isBootingPhase = true;            //创建MountService对象            mMountService = new MountService(getContext());            //通知Binder服务            publishBinderService("mount", mMountService);            //启动mMountService            mMountService.start();            oldDefaultPath = sSelf.getDefaultPath();            Slog.d(TAG, "get Default path onStart default path=" + oldDefaultPath);        }        ......    }

  上面创建了MountService对象,并通知Binder服务的大管家ServiceManager,MountService的服务名为“mount”,对应服务对象为mMountService。通知之后,其他地方需要MountService的服务时便可以通过服务名来向ServiceManager来查询具体的MountService服务。

二、MountService的启动
  上面介绍了SystemServer创建MountService的流程,下面将详细介绍MountService的启动流程,先看MountService的构造函数:

frameworks/base/services/core/java/com/android/server/MountService.java    public MountService(Context context) {        sSelf = this;        mContext = context;        //FgThread线程名为“"android.fg",创建IMountServiceListener回调方法        mCallbacks = new Callbacks(FgThread.get().getLooper());        mLockPatternUtils = new LockPatternUtils(mContext);        //获取PackageManagerService的Client端对象,管理服务        mPms = (PackageManagerService) ServiceManager.getService("package");         //创建“MountService”线程,处理消息        HandlerThread hthread = new HandlerThread(TAG);        hthread.start();        //MountService中的消息处理运行在hthread线程中        mHandler = new MountServiceHandler(hthread.getLooper());        //IoThread线程名为"android.io",创建OBB操作的handler        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());        //判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间        File dataDir = Environment.getDataDirectory();                File systemDir = new File(dataDir, "system");               mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);        if (!mLastMaintenanceFile.exists()) {            // Not setting mLastMaintenance here means that we will force an            // fstrim during reboot following the OTA that installs this code.            try {                (new FileOutputStream(mLastMaintenanceFile)).close();            } catch (IOException e) {                Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());            }        } else {            mLastMaintenance = mLastMaintenanceFile.lastModified();        }        mSettingsFile = new AtomicFile(                new File(Environment.getDataSystemDirectory(), "storage.xml"));        synchronized (mLock) {            readSettingsLocked();        }        //将MountServiceInternalImpl登记到sLocalServiceObjects        LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);        /*         * Create the connection to vold with a maximum queue of twice the         * amount of containers we'd ever expect to have. This keeps an         * "asec list" from blocking a thread repeatedly.         */        //创建用于VoldConnector的NativeDaemonConnector对象        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, null);        mConnector.setDebug(true);        //创建线程名为"VoldConnector"的线程,用于跟vold通信        mConnector.setWarnIfHeld(mLock);        mConnectorThread = new Thread(mConnector, VOLD_TAG);        //创建用于CryptdConnector的NativeDaemonConnector对象        // Reuse parameters from first connector since they are tested and safe        mCryptConnector = new NativeDaemonConnector(this, "cryptd",                MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);        mCryptConnector.setDebug(true);        //创建线程名为"CryptdConnector"的线程,用于加密        mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);        //注册监听用户添加、删除的广播        final IntentFilter userFilter = new IntentFilter();        userFilter.addAction(Intent.ACTION_USER_ADDED);        userFilter.addAction(Intent.ACTION_USER_REMOVED);        userFilter.addAction(Intent.ACTION_USER_SWITCHED);        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);        synchronized (mLock) {            addInternalVolumeLocked();        }        // Add ourself to the Watchdog monitors if enabled.        //默认为false        if (WATCHDOG_ENABLE) {            Watchdog.getInstance().addMonitor(this);        }        initMTKFeature();    }

其中MountService完成的主要任务如下:
1、创建ICallbacks回调方法,FgThread线程名为”android.fg”;
3、创建并启动线程名为”MountService”的handlerThread用来处理消息;
4、创建OBB操作的handler,IoThread线程名为”android.io“;
5、创建NativeDaemonConnector对象;
6、创建并启动线程名为”VoldConnector”的线程;
7、创建并启动线程名为”CryptdConnector”的线程;
8、注册监听用户添加、删除的广播;

  由上可以看出Java层与MountService相关主要线程如下:

//ps -t |grep 804system    804   442   1987512 155668 SyS_epoll_ 73d36167a0 S system_serversystem    891   804   1987512 155668 SyS_epoll_ 73d36167a0 S android.fgsystem    892   804   1987512 155668 SyS_epoll_ 73d36167a0 S android.iosystem    1083  804   1987512 155668 SyS_epoll_ 73d36167a0 S MountServicesystem    1084  804   1987512 155668 unix_strea 73d3617328 S VoldConnectorsystem    1085  804   1987512 155668 unix_strea 73d3617328 S CryptdConnector

  同时在native层中的守护进程vold在系统中显示如下,可通过ps -t |grep -nr vold查看系统线程:

USER      PID   PPID  VSIZE  RSS   WCHAN              PC     NAMEroot      255   1     62588  4488  hrtimer_na 7a06573190 S   /system/bin/voldroot      258   255   62588  4488  poll_sched 7a065728d8 S   voldroot      259   255   62588  4488  poll_sched 7a065728d8 S   voldroot      260   255   62588  4488  poll_sched 7a065728d8 S   voldmedia_rw  1201  255   21280  2712  inotify_re 709185d298 S   /system/bin/sdcard

三、MountService的指令发送
  上面介绍了MountService的启动,和一系列与MountService相关的线程的创建,下面分析MountService命令的传输,首先Vold作为存储设备的管控中心,需要接收来自上层MountService的操作命令,MountService驻留在SystemServer进程中,和Vold作为两个不同的进程,它们之间的通信方式采用的是socket通信,而在MountService这端,同样启动了VoldConnector socket连接线程,用于循环连接服务端,保证连接不被中断,当成功连接Vold时,循环从服务端读取数据。MountService按照指定格式向Vold发送命令,由于发送的命令比较多,这里不做一一接收,只对其中的mount命令的发送流程进行介绍,首先看下面的时序图:
这里写图片描述
  下面看详细的代码:
  ./frameworks/base/services/core/java/com/android/server/MountService.java

    public int mountVolume(String path) {        Slog.i(TAG, "mountVolume, path=" + path);        mount(findVolumeIdForPathOrThrow(path));        return 0;    }

  调用自身的mount来传送挂载命令:

    public void mount(String volId) {        //权限检验         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);        waitForReady();        final VolumeInfo vol = findVolumeByIdOrThrow(volId);        Slog.i(TAG, "mount, volId=" + volId + ", volumeInfo=" + vol);        if (isMountDisallowed(vol)) {            throw new SecurityException("Mounting " + volId + " restricted by policy");        }        try {            //调用execute将命令交给NativeDaemonConnector去发送              mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);        } catch (NativeDaemonConnectorException e) {            //throw e.rethrowAsParcelableException();            Slog.e(TAG, "mount" + vol + "ERROR!!");        }    }

  下面继续看线程NativeDaemonConnector中所做的工作:

frameworks\base\services\core\java\com\android\server\NativeDaemonConnector.java    public NativeDaemonEvent execute(String cmd, Object... args)            throws NativeDaemonConnectorException {        //设置超时时间:DEFAULT_TIMEOUT = 1 * 60 * 1000        return execute(DEFAULT_TIMEOUT, cmd, args);    }    public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)            throws NativeDaemonConnectorException {        //调用executeForList发送命令        final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);        if (events.length != 1) {            throw new NativeDaemonConnectorException(                    "Expected exactly one response, but received " + events.length);        }        return events[0];    }

  在executeForList中首先判断命令的合法性,并对命令进行处理后写入到socket输出流mOutputStream中。

    public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)            throws NativeDaemonConnectorException {        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {            loge("Calling thread " + Thread.currentThread().getName() + " is holding 0x"                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)));        }        //获取数据传输的开始时间        final long startTime = SystemClock.elapsedRealtime();        final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();        final StringBuilder rawBuilder = new StringBuilder();        final StringBuilder logBuilder = new StringBuilder();        //mSequenceNumber初始化值为0,每执行一次该方法则进行加1操作        final int sequenceNumber = mSequenceNumber.incrementAndGet();        //对指令进行处理        makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);        final String rawCmd = rawBuilder.toString();        final String logCmd = logBuilder.toString();        //eg:VoldConnector: SND -> {13 volume unmount public:179,129}        log("SND -> {" + logCmd + "}");        synchronized (mDaemonLock) {            if (mOutputStream == null) {                throw new NativeDaemonConnectorException("missing output stream");            } else {                try {                    //将命令写入到socket输出流                    mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));                } catch (IOException e) {                    throw new NativeDaemonConnectorException("problem sending command", e);                }            }        }        NativeDaemonEvent event = null;        do {            //设置指令接收时间60S,超过60S跳出循环            event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);            if (event == null) {                loge("timed-out waiting for response to " + logCmd);                throw new NativeDaemonTimeoutException(logCmd, event);            }            if (VDBG) log("RMV <- {" + event + "}");            events.add(event);        //当收到的事件响应码属于[100,200)区间,则继续等待后续事件上报,否则跳出循环        } while (event.isClassContinue());        //获取数据传输的结束时间,并判断是否超过WARN_EXECUTE_DELAY_MS=0.5S        final long endTime = SystemClock.elapsedRealtime();        if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {            loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");        }        if (event.isClassClientError()) {            throw new NativeDaemonArgumentException(logCmd, event);        }        if (event.isClassServerError()) {            throw new NativeDaemonFailureException(logCmd, event);        }        return events.toArray(new NativeDaemonEvent[events.size()]);    }

  以上executeForList是MountService指令传输中的重要函数,其重主要完成的任务总结如下:
(1) 带执行的命令mSequenceNumber执行加1操作;
(2) 将cmd写入到socket的输出流;
(3) 通过循环与poll机制阻塞等待底层响应该操作完成的结果;
   有两个情况会跳出循环:
    a、当超过1分钟未收到vold相应事件的响应码,则跳出阻塞等待;
    b、当收到底层的响应码,且响应码不属于[100,200)区间,则跳出循环。
(4)对于执行时间超过500ms的时间,则额外输出以NDC Command开头的log信息。

  MountService向vold发送消息后,便阻塞在MountService线程的NDC.execute()方法,当MonutService接收到vold返回的消息,且消息响应吗不属于区间[600,700)则添加事件到ResponseQueue,从而唤醒阻塞的MountService继续执行。
  以上接收了MountService发送消息给vold的处理过程下面继续分析MountService接收消息的处理过程。

四、MountService指令接收
  MountService接收来自vold的消息主要分为两种类型:
(1)当MountService向Vold发送命令后,将接收到Vold的响应消息;
(2)当外部存储设备发生热插拔时,kernel将通过netlink方式通知Vold,Vold进程经过一系列处理后最终还是要将uevent事件消息发送给MountService,Vold发送uevent的过程前文[ vold进程二]已经介绍(http://blog.csdn.net/frank_zyp/article/details/56666576)
以上消息类型MountService都是通过VoldConnector线程来循环接收Vold的请求。

1、接收来自vold的返回码
  前面已经介绍当Vold在处理完完MountService发送过来的消息后,会通过sendGenericOkFail发送应答消息给上层的MountService。

//./system/vold/CommandListener.cppint CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {    if (!cond) {        //返回响应码为200的成功应答消息        return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);    } else {        //返回响应码为400的失败应答消息        return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);    }}

  其中返回的ResponseCode主要包含以下几种:

class ResponseCode {public:    //部分响应,随后继续产生事件    // 100 series - Requestion action was initiated; expect another reply    // before proceeding with a new command.    static const int ActionInitiated  = 100;    static const int VolumeListResult         = 110;    static const int AsecListResult           = 111;    static const int StorageUsersListResult   = 112;    static const int CryptfsGetfieldResult    = 113;    //成功响应    // 200 series - Requested action has been successfully completed    static const int CommandOkay              = 200;    static const int ShareStatusResult        = 210;    static const int AsecPathResult           = 211;    static const int ShareEnabledResult       = 212;    static const int PasswordTypeResult       = 213;    static const int CdromStatusResult        = 214;    //本地客户端错误    // 500 series - The command was not accepted and the requested    // action did not take place.    static const int CommandSyntaxError = 500;    static const int CommandParameterError = 501;    static const int CommandNoPermission = 502;    //远程Vold进程自触发的事件,主要是针对disk,volume的一系列操作,    //比如设备创建,状态、路径改变,以及文件类型、uid、标签改变等事件都是底层直接触发。    // 600 series - Unsolicited broadcasts    static const int UnsolicitedInformational       = 600;    static const int VolumeStateChange              = 605;    static const int VolumeMountFailedBlank         = 610;    static const int VolumeMountFailedDamaged       = 611;    static const int VolumeMountFailedNoMedia       = 612;    static const int VolumeUuidChange               = 613;    static const int VolumeUserLabelChange          = 614;    static const int ShareAvailabilityChange        = 620;    static const int VolumeDiskInserted            = 630;    static const int VolumeDiskRemoved             = 631;    static const int VolumeBadRemoval              = 632;    static const int DiskCreated = 640;    static const int DiskSizeChanged = 641;    static const int DiskLabelChanged = 642;    static const int DiskScanned = 643;    static const int DiskSysPathChanged = 644;    static const int DiskDestroyed = 649;    static const int VolumeCreated = 650;    static const int VolumeStateChanged = 651;    static const int VolumeFsTypeChanged = 652;    static const int VolumeFsUuidChanged = 653;    static const int VolumeFsLabelChanged = 654;    static const int VolumePathChanged = 655;    static const int VolumeInternalPathChanged = 656;    static const int VolumeDestroyed = 659;    static const int MoveStatus = 660;    static const int BenchmarkResult = 661;    static const int TrimResult = 662;    static int convertFromErrno();};

  上面的消息通过socket管道发送,最后将消息写入到管道中:

//SocketClient.cppint SocketClient::sendMsg(int code, const char *msg, bool addErrno) {    return sendMsg(code, msg, addErrno, mUseCmdNum);}

  当vold进程将消息写入到管道中后,MountService会有线程NativeDaemonConnector循环的的读取并处理管道中的消息:

//./frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java    public void run() {        //为VoldConnector线程创建一个Handler,用于向该线程分发消息         mCallbackHandler = new Handler(mLooper, this);        //循环监听来自kernel的消息        while (true) {            try {                listenToSocket();//处理并分发消息            } catch (Exception e) {                loge("Error in NativeDaemonConnector: " + e);                SystemClock.sleep(5000);            }        }    }

  下面看处理消息的重要函数listenToSocket:

//./frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java    private void listenToSocket() throws IOException {        LocalSocket socket = null;        try {            //创建Vold socket              socket = new LocalSocket();            LocalSocketAddress address = determineSocketAddress();            //向服务端发起连接请求            socket.connect(address);            //获取连接的socket中得到输入输出流              InputStream inputStream = socket.getInputStream();            synchronized (mDaemonLock) {                mOutputStream = socket.getOutputStream();            }            //对本次连接请求做一些回调处理              mCallbacks.onDaemonConnected();            FileDescriptor[] fdList = null;            byte[] buffer = new byte[BUFFER_SIZE];            int start = 0;            while (true) {                //从socket输出流中读取数据                int count = inputStream.read(buffer, start, BUFFER_SIZE - start);                if (count < 0) {                    loge("got " + count + " reading with start = " + start);                    break;                }                fdList = socket.getAncillaryFileDescriptors();                // Add our starting point to the count and reset the start.                count += start;                start = 0;                //解析读取到的数据,得到NativeDaemonEvent                  for (int i = 0; i < count; i++) {                    if (buffer[i] == 0) {                        // Note - do not log this raw message since it may contain                        // sensitive data                        final String rawEvent = new String(                                buffer, start, i - start, StandardCharsets.UTF_8);                        boolean releaseWl = false;                        try {                            final NativeDaemonEvent event =                                  NativeDaemonEvent.parseRawEvent(rawEvent, fdList);                            log("RCV <- {" + event + "}");                            //如果命令码code >= 600 && code < 700                                 if (event.isClassUnsolicited()) {                                //将读取到的事件发送到VoldConnector.CallbackHandler线程中处理                                if (mCallbacks.onCheckHoldWakeLock(event.getCode())                                        && mWakeLock != null) {                                    mWakeLock.acquire();                                    releaseWl = true;                                }                                                           Message msg = mCallbackHandler.obtainMessage(                                        event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());                                if (mCallbackHandler.sendMessage(msg)) {                                    releaseWl = false;                                }                            } else {                                     //对于其他响应码则添加到mResponseQueue队列                                mResponseQueue.add(event.getCmdNumber(), event);                            }                        } catch (IllegalArgumentException e) {                            log("Problem parsing message " + e);                        } finally {                                 if (releaseWl) {                                mWakeLock.release();                            }                        }                        start = i + 1;                    }                }                if (start == 0) {                    log("RCV incomplete");                }          ......      }                                                              

以上代码中主要完成了两项工作:
(1)当vold返回的响应吗不在区间[600,700),则将该事件添加到mResponseQueue,并且触发响应事件所对应的请求事件不再阻塞到ResponseQueue.poll,那么线程继续往下执行;
(2)当返回的响应码区间为[600,700):则发送消息交由mCallbackHandler处理,向线程android.fg发送Handler消息,该线程收到后回调NativeDaemonConnector的handleMessage来处理。

2、处理设备挂载等命令
  上面介绍了listenToSocket中数据处理的第一种情况,下面将分析来自不请自来的广播的消息。分析之前看看前面文章分析的vold传输命令的过程,如下面的通信流程图:
这里写图片描述
  在NativeDaemonConnector的run函数中,可知消息通过handler消息机制,由mCallbackHandler处理,NativeDaemonConnector的handleMessage如下:

    public boolean handleMessage(Message msg) {    ......    //mCallbacks是由实例化NativeDaemonConnector对象时传递进来的,在这里是指MountService            if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {                log(String.format("Unhandled event '%s'", event));            }    ......

  接着看MountService的onEvent方法:

    //./frameworks/base/services/core/java/com/android/server/MountService.java    public boolean onEvent(int code, String raw, String[] cooked) {        synchronized (mLock) {            return onEventLocked(code, raw, cooked);        }    }    private boolean onEventLocked(int code, String raw, String[] cooked) {    //处理各种设备挂载等的命令    ......        switch (code) {            case VoldResponseCode.DISK_CREATED: {                Slog.d(TAG, "DISK_CREATED");                if (cooked.length != 3) break;                final String id = cooked[1];                int flags = Integer.parseInt(cooked[2]);    ..                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)                        || mForceAdoptable) {                    flags |= DiskInfo.FLAG_ADOPTABLE;                }                mDisks.put(id, new DiskInfo(id, flags));                Slog.d(TAG, "create diskInfo=" + mDisks.get(id));                isDiskInsert = true;                break;                .......            }    ......    }

  在MountService中采用 1个主线程(system_server) + 3个子线程(VoldConnector, MountService, CryptdConnector);MountService线程不断向vold下发存储相关的命令,比如mount, mkdirs等操作;而线程VoldConnector一直处于阻塞接收vold发送过来的应答事件并进行处理。

作者:frank_zyp
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。

0 0