Android7.0 SD卡挂载流程


序言:这是7.0时候的总结,8.0MountService改名成了StorageManageService,懒得再重新总结,直接把7.0的总结贴上


这篇文章是对上层T卡挂载的流程总结,涉及到的主要类介绍:

MountService:Android Binder服务,运行在system_server进程,用于跟Vold进行消息通信,比如 MountService 向 Vold 发送挂载SD卡的命令,或者接收到来自 Vold 的外设热插拔事件。

NativeDaemonConnector(Client端)和 Vold的CL模块(Server端)建立socket通信。

StorageManager存储管理类。

1.    MountService启动

MountService运行在system_server进程,在系统启动到阶段

在 SystemServer.java--->startOtherServices()中:

/*
                     * NotificationManagerService is dependant on MountService,
                     * (for media / usb notifications) so we must start MountService first.
                     */
                    mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);
                    mountService = IMountService.Stub.asInterface(
                            ServiceManager.getService("mount"));



private static final String MOUNT_SERVICE_CLASS=

           "com.android.server.MountService$Lifecycle";

mSystemServiceManager.startService(MOUNT_SERVICE_CLASS)完成创建MOUNT_SERVICE_CLASS所指类的Lifecycle对象,将该对象添加SystemServiceManager的 mServices 服务列表,最后调用Lifecycle的onStart()方法.

Lifecycle是MountService的一个内部类:

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;
            mMountService = new MountService(getContext());
            publishBinderService("mount", mMountService);
            mMountService.start();
            oldDefaultPath = sSelf.getDefaultPath();
            Slog.d(TAG, "get Default path onStart default path=" + oldDefaultPath);
        }
     ......
}

在Lifecycle的onStart()创建MountService对象

2.    NativeDaemonConnector启动

NativeDaemonConnector实现了Runnable, Handler.Callback, Watchdog.Monitor三个接口

在MountService的构造方法中创建NativeDaemonConnector对象:

mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
                null);
        mConnector.setDebug(true);
        mConnector.setWarnIfHeld(mLock);
        mConnectorThread = new Thread(mConnector, VOLD_TAG);


创建名为mConnectorThread的线程,用于和vold的进行通信


@Override
    public void run() {
        mCallbackHandler = new Handler(mLooper, this);

        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }

在run()中通过while(true)不断调用listenToSocket()来监听vold的socket

MountService向vold发送信息

1.    onBootPhase

MountService与NDC都启动,那么接下来到系统启动到达阶段PHASE_ACTIVITY_MANAGER_READY,则调用到onBootPhase方法。



@Override
        public void onBootPhase(int phase) {
            Slog.d(TAG, "MountService onBootPhase");
            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mMountService.systemReady();
            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                mMountService.bootCompleted();				
            }
            if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                Slog.d(TAG, "MountService onBootPhase: PHASE_BOOT_COMPLETED");
                sSelf.isBootingPhase = false;
                if (!oldDefaultPath.contains("emulated") && !"".equals(oldDefaultPath)) {
                    Slog.d(TAG, "set defaut path to " + oldDefaultPath);
                    sSelf.setDefaultPath(oldDefaultPath);
					sSelf.updateDefaultPathIfNeed();
                }
            }
        }



调用MountService.systemReady方法,该方法主要是通过mHandler发送消息。



private void systemReady() {
        mSystemReady = true;
        mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
    }

mHandler = new MountServiceHandler(hthread.getLooper());

到此system_server主线程通过handler向线程”MountService”发送H_SYSTEM_READY消息,接下来进入线程”MountService”的MountServiceHandler对象的handleMessage()来处理相关的消息。



2.    调用handleSystemReady

private void handleSystemReady() {
        initIfReadyAndConnected();
        resetIfReadyAndConnected();

        /*
         * If UMS was connected on boot
         * send the connected broadcast when system ready
         */
        if (mSendUmsConnectedOnBoot) {
            sendUmsIntent(true);
            mSendUmsConnectedOnBoot = false;
        }

        // Start scheduling nominally-daily fstrim operations
        MountServiceIdler.scheduleIdlePass(mContext);
    }



3.    调用resetIfReadyAndConnected()调用NDC.execute

private void resetIfReadyAndConnected() {
        Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
                + ", mDaemonConnected=" + mDaemonConnected);
        if (mSystemReady && mDaemonConnected) {
            final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
            killMediaProvider(users);

            final int[] systemUnlockedUsers;
            synchronized (mLock) {
                systemUnlockedUsers = mSystemUnlockedUsers;

                mDisks.clear();
                mVolumes.clear();

                addInternalVolumeLocked();
            }

            try {
                mConnector.execute("volume", "reset");

                // Tell vold about all existing and started users
                for (UserInfo user : users) {
                    mConnector.execute("volume", "user_added", user.id, user.serialNumber);
                }
                for (int userId : systemUnlockedUsers) {
                    mConnector.execute("volume", "user_started", userId);
                }
            } catch (NativeDaemonConnectorException e) {
                Slog.w(TAG, "Failed to reset vold", e);
            }
        }
    }

4.     NDC.execute

public NativeDaemonEvent execute(String cmd, Object... args)
            throws NativeDaemonConnectorException {
        return execute(DEFAULT_TIMEOUT, cmd, args);
    }

    public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
            throws NativeDaemonConnectorException {
        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];
    }



MountService接收vold信息



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

        try {
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();

            socket.connect(address);

            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) {
                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;

                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 + "}");

                            if (event.isClassUnsolicited()) {
                                // TODO: migrate to sending NativeDaemonEvent instances
                                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.add(event.getCmdNumber(), event);
                            }
                        } catch (IllegalArgumentException e) {
                            log("Problem parsing message " + e);
                        } finally {
                            if (releaseWl) {
                                mWakeLock.release();
                            }
                        }

                        start = i + 1;
                    }
                }



通过handler消息机制,由mCallbackHandler处理,先来看看其初始化:



@Override
    public boolean handleMessage(Message msg) {
        final String event = (String) msg.obj;
        final int start = uptimeMillisInt();
        final int sent = msg.arg1;
        try {
            if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
                log(String.format("Unhandled event '%s'", event));
            }
        } catch (Exception e) {
            loge("Error handling '" + event + "': " + e);
        } finally {
            if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
                mWakeLock.release();
            }
            final int end = uptimeMillisInt();
            if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
                loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
            }
            if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
                loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
            }
        }
        return true;
    }


onEevent 是接口InativeDaemonConnectorCallbacks中的一个抽象方法,其在MountService实现



2.    MS.onEvent
@Override
    public boolean onEvent(int code, String raw, String[] cooked) {
        synchronized (mLock) {
            return onEventLocked(code, raw, cooked);
        }
    }



MountService.onEvent调用了onEventLocked,onEventLocked增加了同步锁,用于多线程并发访问处理,根据vold发送过来的不同响应码采取不同的处理流程



3.     MS. onEventLocked


当收到的响应码是650时


case VoldResponseCode.VOLUME_CREATED: {
                Slog.d(TAG, "VOLUME_CREATED");
                final String id = cooked[1];
                final int type = Integer.parseInt(cooked[2]);
                final String diskId = TextUtils.nullIfEmpty(cooked[3]);
                final String partGuid = TextUtils.nullIfEmpty(cooked[4]);

                final DiskInfo disk = mDisks.get(diskId);
                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
                mVolumes.put(id, vol);
                onVolumeCreatedLocked(vol);
                Slog.d(TAG, "create volumeInfo=" + mVolumes.get(id));
                break;
            }

将T卡信息put到VolumeInfo中,T卡挂载成功,同时调用onVolumeCreatedLocked()方法

private void onVolumeCreatedLocked(VolumeInfo vol) {
        if (mPms.isOnlyCoreApps()) {
            Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
            return;
        }
        Slog.d(TAG, "onVolumeCreatedLocked, volumeInfo=" + vol);
        if (vol.type == VolumeInfo.TYPE_EMULATED) {
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);

            Slog.d(TAG, "privateVol=" + privateVol);
            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();

            } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
            }
        ......
}
case H_VOLUME_MOUNT: {
                    Slog.i(TAG, "H_VOLUME_MOUNT");
                    final VolumeInfo vol = (VolumeInfo) msg.obj;
                    if (isMountDisallowed(vol)) {
                        Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
                        break;
                    }
                    int rc = StorageResultCode.OperationSucceeded;
                    try {
                        mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
                                vol.mountUserId);
                    } catch (NativeDaemonConnectorException ignored) {
                        rc = ignored.getCode();
                        Slog.w(TAG, "mount volume fail, ignored=" + ignored);
                    }
                    if (rc == StorageResultCode.OperationSucceeded) {
                        VolumeInfo curVol = null;
                        synchronized (mLock) {
                            curVol = mVolumes.get(vol.getId());
                        }
                        if (isShowDefaultPathDialog(curVol)) {
                            showDefaultPathDialog(curVol);
                        }
                    } else {
                        // if mount fail, for other volume mount operation
                        // should not marked as disk insert
                        isDiskInsert = false;
                        Slog.w(TAG, "mount volume fail, vol=" + vol
                                + ", return code=" + rc);
                    }
                    break;
                }



4. T卡状态的改变



当MS. onEventLocked收到的响应码是651时



case VoldResponseCode.VOLUME_STATE_CHANGED: {
                Slog.d(TAG, "VOLUME_STATE_CHANGED");
                if (cooked.length != 3) break;
                final VolumeInfo vol = mVolumes.get(cooked[1]);
                if (vol != null) {
                    final int oldState = vol.state;
                    final int newState = Integer.parseInt(cooked[2]);
                    vol.state = newState;
                    onVolumeStateChangedLocked(vol, oldState, newState);
                }
                break;
            }



private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
        Slog.d(TAG, "onVolumeStateChangedLocked"
                + ", oldState=" + VolumeInfo.getEnvironmentForState(oldState)
                + ", newState=" + VolumeInfo.getEnvironmentForState(newState)
                + ", volumeInfo=" + vol);
        // Remember that we saw this volume so we're ready to accept user
        // metadata, or so we can annoy them when a private volume is ejected
        if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
            VolumeRecord rec = mRecords.get(vol.fsUuid);
            if (rec == null) {
                rec = new VolumeRecord(vol.type, vol.fsUuid);
                rec.partGuid = vol.partGuid;
                rec.createdMillis = System.currentTimeMillis();
                if (vol.type == VolumeInfo.TYPE_PRIVATE) {
                    rec.nickname = vol.disk.getDescription();
                }
                mRecords.put(rec.fsUuid, rec);
                writeSettingsLocked();
            } else {
                // Handle upgrade case where we didn't store partition GUID
                if (TextUtils.isEmpty(rec.partGuid)) {
                    rec.partGuid = vol.partGuid;
                    writeSettingsLocked();
                }
            }
        }

        mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);



private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
            final SomeArgs args = SomeArgs.obtain();
            args.arg1 = vol.clone();
            args.argi2 = oldState;
            args.argi3 = newState;
            obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
        }
private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
                throws RemoteException {
            switch (what) {
                case MSG_STORAGE_STATE_CHANGED: {
                    callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
                            (String) args.arg3);
                    break;
                }
                case MSG_VOLUME_STATE_CHANGED: {
                    callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
                    break;
                }
                case MSG_VOLUME_RECORD_CHANGED: {
                    callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
                    break;
                }
                case MSG_VOLUME_FORGOTTEN: {
                    callback.onVolumeForgotten((String) args.arg1);
                    break;
                }
                case MSG_DISK_SCANNED: {
                    callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
                    break;
                }
                case MSG_DISK_DESTROYED: {
                    callback.onDiskDestroyed((DiskInfo) args.arg1);
                    break;
                }
                case MSG_UMS_CONNECTION_CHANGED: {
                    callback.onUsbMassStorageConnectionChanged((boolean)args.arg1);
                    break;
                }
            }
        }

可以看到callback是一个接口,下面就看下这个接口是在哪儿实现的:
StorageEventListenerDelegate是StorageManager的内部类,继承

IMountServiceListener.Stub

private static class StorageEventListenerDelegate extends IMountServiceListener.Stub implements
            Handler.Callback {
        private static final int MSG_STORAGE_STATE_CHANGED = 1;
        private static final int MSG_VOLUME_STATE_CHANGED = 2;
        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
        private static final int MSG_VOLUME_FORGOTTEN = 4;
        private static final int MSG_DISK_SCANNED = 5;
        private static final int MSG_DISK_DESTROYED = 6;

        final StorageEventListener mCallback;
        final Handler mHandler;

        public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) {
            mCallback = callback;
            mHandler = new Handler(looper, this);
        }



@Override
        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
            final SomeArgs args = SomeArgs.obtain();
            args.arg1 = vol;
            args.argi2 = oldState;
            args.argi3 = newState;
            mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
        }



case MSG_VOLUME_STATE_CHANGED:
                    mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
                    args.recycle();
                    return true;



可以看到又调用了StorageEventListener这个接口,就是通个这个接口其他进程T卡状态变了!

在Settings中我们可以看到,实现了这个接口:



private final StorageEventListener mStorageListener = new StorageEventListener() {
        @Override
        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
            if (isInteresting(vol)) {
                refresh();
            }
        }

        @Override
        public void onDiskDestroyed(DiskInfo disk) {
            refresh();
        }

        /// M: ALPS02316229 refresh UI when plug in or out SD card.
        @Override
        public void onDiskScanned(DiskInfo disk, int volumeCount) {
            refresh();
            
        }
    };

Settings的T卡状态的监听就是通过StorageEventListener监听实现的



5.     T卡的广播



再回过头看onVolumeStateChangedLocked()




if (!Objects.equals(oldStateEnv, newStateEnv)) {
            // Kick state changed event towards all started users. Any users
            // started after this point will trigger additional
            // user-specific broadcasts.
            for (int userId : mSystemUnlockedUsers) {
                if (vol.isVisibleForRead(userId)) {
                    final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();


                    Slog.d(TAG, "notify callbacks StorageStateChanged, storageVolume=" + userVol);
                    mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
                            newStateEnv);
                }
            }
        }
case H_VOLUME_BROADCAST: {
                    Slog.i(TAG, "H_VOLUME_BROADCAST");
                    final StorageVolume userVol = (StorageVolume) msg.obj;
                    final String envState = userVol.getState();
                    Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to "
                            + userVol.getOwner());

                    final String action = VolumeInfo.getBroadcastForEnvironment(envState);
                    if (action != null) {
                        final Intent intent = new Intent(action,
                                Uri.fromFile(userVol.getPathFile()));
                        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                        Slog.i(TAG, "sendBroadcastAsUser, intent=" + intent
                                + ", userVol=" + userVol);
                        mContext.sendBroadcastAsUser(intent, userVol.getOwner());
                    }
                    break;
                }



重点看下这个action,是用VolumeInfo.getBroadcastForEnvironment中获取到的



public static @Nullable String getBroadcastForEnvironment(String envState) {
        return sEnvironmentToBroadcast.get(envState);
    }
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
        sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
        sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
        sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
        sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
        sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);