Android Framework 存储子系统(02)MountService服务

该系列文章总纲链接:专题分纲目录 Android Framework 存储子系统


本章关键点总结 & 说明:

Android Framework 存储子系统(02)MountService服务_第1张图片

导图是不断迭代的,这里主要关注➕左边的 MountService 部分即可,这里主要分析了MountService的关键流程,接下来分析了连接器 NativeDaemonConnector,最后介绍了OBB文件系统(针对 体积较大的APP)。

MountService是在SystemServer中启动的Binder服务,是让用户进程能通过它 发送命令给vold进程,vold进程再处理各种存储设备的操作。

1 MountService的启动过程

在systemserver中启动的mountservice相关代码如下所示:

private void startOtherServices() {
    //...
    MountService mountService = null;
    //...
    try {
        mountService = new MountService(context);
        ServiceManager.addService("mount", mountService);
    }
    //...
    final MountService mountServiceF = mountService;
    //...
    try {
        if (mountServiceF != null) mountServiceF.systemReady();
    } 
	//...
}

这里关键分析MountService构造器,代码如下:

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

    //活得PkgMS 引用
    mPms = (PackageManagerService) ServiceManager.getService("package");
    //创建处理消息的线程
    HandlerThread hthread = new HandlerThread(TAG);
    hthread.start();
    mHandler = new MountServiceHandler(hthread.getLooper());

    //监听用户变化的intent
    final IntentFilter userFilter = new IntentFilter();
    userFilter.addAction(Intent.ACTION_USER_ADDED);
    userFilter.addAction(Intent.ACTION_USER_REMOVED);
    mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

    // 监听USB状态的变化
    final StorageVolume primary = getPrimaryPhysicalVolume();
    if (primary != null && primary.allowMassStorage()) {
        mContext.registerReceiver(
            mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
    }

    // 创建OBB Action的处理对象
    mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());

    //...
    //创建和vold连接的NativeDaemonConnector
    mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,null);
    //开启线程,处理NativeDaemonConnector对象和底层的连接
    Thread thread = new Thread(mConnector, VOLD_TAG);
    thread.start();

    //看门狗注册
    if (WATCHDOG_ENABLE) {
        Watchdog.getInstance().addMonitor(this);
    }
}

MountService的4个关键工作:

  1. 获得PkgMS引用,因为底层存储的变化会导致PkgMS重新扫描,因此MountService收到底层消息会通知PkgMS
  2. 监听几种关键的event:用户变化,usb状态变化,系统空闲
  3. 创建OBB Action的处理对象,处理与obb文件系统相关消息
  4. 创建和vold进行socket连接的NativeDaemonConnector,创建新线程执行NativeDaemonConnector对象的run方法

2 NativeDaemonConnector分析

NativeDaemonConnector是通过socket方式和vold进程进行通信

2.1 构造函数如下所示:

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
        int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
    this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl,
            FgThread.get().getLooper());
}

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
        int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,
        Looper looper) {
    mCallbacks = callbacks;//这个callback就是mountService
    mSocket = socket;//字符串vold
    mResponseQueue = new ResponseQueue(responseQueueSize);
    mWakeLock = wl;
    if (mWakeLock != null) {
        mWakeLock.setReferenceCounted(true);
    }
    mLooper = looper;
    mSequenceNumber = new AtomicInteger(0);
    TAG = logTag != null ? logTag : "NativeDaemonConnector";
    mLocalLog = new LocalLog(maxLogSize);
}

构造函数主要就是初始化一些变量,特变关注上面提到的mSocket和mCallbacks,因为直接看并不容易被理解。

2.2 run函数

因为是使用线程来进行运行,因此这里还要关注run的运行,代码如下:

@Override
public void run() {
    mCallbackHandler = new Handler(mLooper, this);
    while (true) {
        try {
            listenToSocket();
        } catch (Exception e) {
            loge("Error in NativeDaemonConnector: " + e);
            SystemClock.sleep(5000);
        }
    }
}

继续分析关键函数listenToSocket,代码实现如下:

private void listenToSocket() throws IOException {
    LocalSocket socket = null;

    try {
        socket = new LocalSocket();
        LocalSocketAddress address = determineSocketAddress();
        socket.connect(address); //连接vold的socket
        InputStream inputStream = socket.getInputStream();
        synchronized (mDaemonLock) {
            mOutputStream = socket.getOutputStream();
        }
        mCallbacks.onDaemonConnected();

        byte[] buffer = new byte[BUFFER_SIZE];
        int start = 0;

        while (true) {
            //从socket中读取数据,没数据时会挂起
            int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
            //...错误处理
            count += start;
            start = 0;

            for (int i = 0; i < count; i++) {
                if (buffer[i] == 0) {//每条vold发送的消息都以\0结尾
                    final String rawEvent = new String(
                            buffer, start, i - start, StandardCharsets.UTF_8);

                    boolean releaseWl = false;
                    try {//解析命令
                        final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
                                rawEvent);
                        if (event.isClassUnsolicited()) {
                            // 检查 处理该消息时,是否禁止系统休眠
                            // 处理完成后 解除休眠
                            if (mCallbacks.onCheckHoldWakeLock(event.getCode())
                                    && mWakeLock != null) {
                                mWakeLock.acquire();//禁止系统休眠
                                releaseWl = true;
                            }
                            //如果收到底层消息,转发给MountService
                            if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                    event.getCode(), event.getRawEvent()))) {
                                releaseWl = false;
                            }
                        } else {
                            //如果收到返回结果,放到mResponseQueue中
                            mResponseQueue.add(event.getCmdNumber(), event);
                        }
                    } catch (IllegalArgumentException e) {
                        log("Problem parsing message " + e);
                    } finally {
                        if (releaseWl) {
                            mWakeLock.acquire();
                        }
                    }
                    start = i + 1;
                }
            }
            if (start != count) {
                final int remaining = BUFFER_SIZE - start;
                System.arraycopy(buffer, start, buffer, 0, remaining);
                start = remaining;
            } else {
                start = 0;
            }
        }
    } catch (IOException ex) {
        //...
    } finally {
        //错误处理 & socket关闭处理
    }
}

前面主要和socket “vold”进行本地连接的创建,连接后调用 listenToSocket 方法进入到一个无限循环,不断读取并且通过NativeDaemonEvent.parseRawEvent将数据解析成NativeDaemonEvent对象,从vold消息中收到的消息有两种:

  1. vold主动发送消息通知;mCallbackHandler主动把消息对象发送出去。
  2. mountService发送消息给vold,vold进行消息回复;先放到mResponseQueue消息队列中,在NativeDaemonEvent的execute方法结束时把消息作为返回值返回。

3 OBB文件系统

android应用的体积变得越来越大,超过一定限度就会安装失败。很多大型APP应用代码不大,但资源体积过大,(如果我们设计一款资源包含比较多的游戏,可能你会发现最终生成的APK文件可能高达300MB,但是APK文件很大导致Android系统无法正常安装,而这么大其实都是游戏中用到的资源文件,我们放到SD卡上可能其他应用也可以访问,比如说系统的图片管理器会索引游戏中的图片资源,而音乐播放器也会索引资源中的音乐,所以OBB文件(Opaque Binary Blob)可以很好的解决大文件在SD卡上分离出APK文件,同时别的程序没有权限访问这样一种隔离的文件系统)那么可以把资源文件放到一个obb的文件中 单独下载,需要时通过mount方式把文件挂载进系统,挂载到系统的文件。

mountService提供了下列与OBB相关的接口:

String  getMountedObbPath(String filename) //通过一个文件名判断挂载的OBB文件路径
boolean  isObbMounted(String filename) //这个OBB文件是否挂载了
boolean  mountObb(String filename, String key, OnObbStateChangeListener listener)  //挂载一个OBB文件
boolean  unmountObb(String filename, boolean force, OnObbStateChangeListener listener)  //反挂载一个obb文件,这个方法是异步的,第二个参数可以强制反挂载。

这里以mountObb为例,分析从mountService到VolumeManager之间数据传递过程,MountService的mountObb方法定义如下:

@Override
public void mountObb(
        String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
    Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
    Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
    Preconditions.checkNotNull(token, "token cannot be null");

    final int callingUid = Binder.getCallingUid();
    //创建ObbState对象
    final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
    //创建ObbAction对象
    final ObbAction action = new MountObbAction(obbState, key, callingUid);
    //发送消息OBB_RUN_ACTION
    mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
}

这里继续分析handler消息是如何处理的,mObbActionHandler处理消息代码如下:


@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case OBB_RUN_ACTION: {
            final ObbAction action = (ObbAction) msg.obj;
            if (!mBound) {//没有连接
                if (!connectToService()) {//连接服务DefaultContainerService
                    action.handleError();
                    return;
                }
            }
            mActions.add(action);加入mActions列表
            break;
        }
        //...
    }
}

这里当connectService建立连接时,会调用到onServiceConnected方法,实现如下:

class DefaultContainerConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service) {
        //...
        IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
        mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
    }
    //...
};

这里会发送OBB_MCS_BOUND消息,相关处理如下:

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        //...
        case OBB_MCS_BOUND: {
            if (msg.obj != null) {
                //保存IMediaContainerService对象
                mContainerService = (IMediaContainerService) msg.obj;
            }
            if (mContainerService == null) {
                //处理错误
            } else if (mActions.size() > 0) {
                final ObbAction action = mActions.get(0);
                if (action != null) {
                    action.execute(this);//执行action
                }
            break;
        }
        //...
    }
}

这里主要是取出消息,并调用其execute函数,相关代码如下:

public void execute(ObbActionHandler handler) {
    try {
        //...
        if (mRetries > MAX_RETRIES) {
            //...错误处理
        } else {
            handleExecute();//继续处理
            mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);//断开服务
        }
    } catch (RemoteException e) {
        //...错误处理
    }
}

这里继续分析handleExecute()函数,代码如下:

@Override
public void handleExecute() throws IOException, RemoteException {
    //...
    int rc = StorageResultCode.OperationSucceeded;
    try {
        //向vold发送消息
        mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
                mObbState.ownerGid);
    } catch (NativeDaemonConnectorException e) {
        //...
    }
    //...
}

这里继续分析mConnector.execute方法,因为mConnector是NativeDaemonConnector,execute方法代码如下:

public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
        throws NativeDaemonConnectorException {
    //...
    synchronized (mDaemonLock) {
        if (mOutputStream == null) {
            throw new NativeDaemonConnectorException("missing output stream");
        } else {
            try {
                //发送消息给vold
                mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                throw new NativeDaemonConnectorException("problem sending command", e);
            }
        }
    }

    NativeDaemonEvent event = null;
    do {
        //检查mResponseQueue队列中的event,只要有就都直接返回
        event = mResponseQueue.remove(sequenceNumber, timeout, logCmd);
        //错误处理...
        events.add(event);
    } while (event.isClassContinue());
    //...
    return events.toArray(new NativeDaemonEvent[events.size()]);
}

这里mOutputStream.write执行后,vold会通过CommandListener对象来处理mountObb消息,runCommand函数的处理如下:

int CommandListener::ObbCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    //...
    VolumeManager *vm = VolumeManager::Instance();
    int rc = 0;

    if (!strcmp(argv[1], "list")) {//list消息处理
        dumpArgs(argc, argv, -1);

        rc = vm->listMountedObbs(cli);
    } else if (!strcmp(argv[1], "mount")) {//mount消息处理
            dumpArgs(argc, argv, 3);
            if (argc != 5) {
                cli->sendMsg(ResponseCode::CommandSyntaxError,
                        "Usage: obb mount   ", false);
                return 0;
            }
            //关键处理函数
            rc = vm->mountObb(argv[2], argv[3], atoi(argv[4]));
    } else if (!strcmp(argv[1], "unmount")) {
        //各种命令处理...
    } else {
        dumpArgs(argc, argv, -1);
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown obb cmd", false);
    }
    //...
    return 0;
}

这里最后是调用了vm->mountObb函数来处理工作,最后总结下mountObb的流程,如下所示:

Android Framework 存储子系统(02)MountService服务_第2张图片

 

你可能感兴趣的:(framework,android,存储子系统)