StorageManagerService流程分析

附录

本人前一阵子一直在学习文件系统方面的知识,都说好记性不如烂笔头,我决定从今天开始总结学习到的知识,和大家一起学习,一起进步。

前言

Android外部存储空间由 vold init 服务和 StorageManagerService 系统服务共同管理。外部实体存储卷的装载由 vold 处理:通过执行分阶段操作准备好媒体,然后再将其提供给应用。
从 Android 8.0 开始,MountService 类已更名为 StorageManagerService。
并且StorageManagerService与Vold的通信方式由socket方式变为binder方式,精简了部分代码,以及之前处理信息交互的CommandListener类也被取消了。

本文涉及代码路径

SystemServer.java
StorageManagerService.java

StorageManagerService

StorageManagerService的启动

SystemService

StorageManagerService同其他系统服务一样,也是从SystemServer启动

//在SystemServer的startOtherServices中,storageManagerservice启动。
  private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ...
                if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
                t.traceBegin("StartStorageManagerService");
                try {
                    /*
                     * NotificationManagerService is dependant on StorageManagerService,
                     * (for media / usb notifications) so we must start StorageManagerService first.
                     */
                 mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS);
                    storageManager = IStorageManager.Stub.asInterface(
                            ServiceManager.getService("mount"));
                } catch (Throwable e) {
                    reportWtf("starting StorageManagerService", e);
                }
                t.traceEnd();

                t.traceBegin("StartStorageStatsService");
                try {
                    mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS);
                } catch (Throwable e) {
                    reportWtf("starting StorageStatsService", e);
                }
                t.traceEnd();
            }
        }
        ...
    }

NotificationManagerService依赖于StorageManagerService,所以必需先启动StroageManagerService。

StorageManagerService服务分析

由Lifecycle启动:

private static final String  STORAGE_MANAGER_SERVICE_CLASS = "com.android.server.StorageManagerService$Lifecycle";

SystemManagerService通过反射构建LifeCycle并调用onStart()方法

public void onStart() {
            mStorageManagerService = new StorageManagerService(getContext());
            publishBinderService("mount", mStorageManagerService);
            mStorageManagerService.start();
        }
 public StorageManagerService(Context context) {
        sSelf = this;

        // 前面先是读取一些属性状态,其中关于FUSE下面会稍微介绍一下
        // Snapshot feature flag used for this boot
        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));

        // If there is no value in the property yet (first boot after data wipe), this value may be
        // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
        // different*
        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
        mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
                ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
        mContext = context;
        mResolver = mContext.getContentResolver();
        mCallbacks = new Callbacks(FgThread.get().getLooper());
        mLockPatternUtils = new LockPatternUtils(mContext);
        // 创建名为“StorageManagerService”的线程,并创建对应的Handler
        HandlerThread hthread = new HandlerThread(TAG);
        hthread.start();
         //新建handle,多数操作经此流转
        mHandler = new StorageManagerServiceHandler(hthread.getLooper());
        // mObbActionHandler对应“android.io”线程
        // Add OBB Action Handler to StorageManagerService thread.
        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());

        mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);

        // 启动installd服务
        mInstaller = new Installer(mContext);
        mInstaller.onStart();

        // Initialize the last-fstrim tracking if necessary
        //如果需要,初始化last-fstrim跟踪  
        File dataDir = Environment.getDataDirectory();
        File systemDir = new File(dataDir, "system");
        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
        //判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间
        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();
        }

        // 读取data/system/storage.xml配置
        mSettingsFile = new AtomicFile(
                new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");

        synchronized (mLock) {
            readSettingsLocked();
        }

        LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal);
 //新建vold与cryptd线程,大部分与存储有关操作都是通过这两个线程与远程socket通信完成,会在连接成功和有消息返回时候回调
    mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
            null);
    mConnector.setDebug(true);
    mConnector.setWarnIfHeld(mLock);
    mConnectorThread = new Thread(mConnector, VOLD_TAG);

        // 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);
        mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);

        final IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_ADDED);
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

        synchronized (mLock) {
            addInternalVolumeLocked();
        }

        //添到watchdog监视队列中
        if (WATCHDOG_ENABLE) {
            Watchdog.getInstance().addMonitor(this);
        }
    }

构造函数完成后立即启动vold跟cryptd线程:

private void start() {
        mConnectorThread.start();
        mCryptConnectorThread.start();
    }

NativeDaemonConnector:启动后连接并且监听socket返回信息

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

        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }
    
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();
            }
            //连接上socket之后回调storagemanagerservice
            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;
                    }
                }

                if (start == 0) {
                    log("RCV incomplete");
                }

                // We should end at the amount we read. If not, compact then
                // buffer and read again.
                if (start != count) {
                    final int remaining = BUFFER_SIZE - start;
                    System.arraycopy(buffer, start, buffer, 0, remaining);
                    start = remaining;
                } else {
                    start = 0;
                }
            }
        } catch (IOException ex) {
            loge("Communications error: " + ex);
            throw ex;
        } finally {
            synchronized (mDaemonLock) {
                if (mOutputStream != null) {
                    try {
                        loge("closing stream for " + mSocket);
                        mOutputStream.close();
                    } catch (IOException e) {
                        loge("Failed closing output stream: " + e);
                    }
                    mOutputStream = null;
                }
            }

            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException ex) {
                loge("Failed closing socket: " + ex);
            }
        }
    }

与vold跟crypted通信返回的数据格式都是使用的:NativeDaemonEvent,通过将从数据流中获取到的string资源解析成NativeDaemonEvent对象。

private NativeDaemonEvent(int cmdNumber, int code, String message,
                              String rawEvent, String logMessage, FileDescriptor[] fdList) {
        mCmdNumber = cmdNumber;
        mCode = code;
        mMessage = message;
        mRawEvent = rawEvent;
        mLogMessage = logMessage;
        mParsed = null;
        mFdList = fdList;
    }
    
public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
        final String[] parsed = rawEvent.split(" ");
        if (parsed.length < 2) {
            throw new IllegalArgumentException("Insufficient arguments");
        }

        int skiplength = 0;

        final int code;
        try {
            code = Integer.parseInt(parsed[0]);
            skiplength = parsed[0].length() + 1;
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("problem parsing code", e);
        }

        int cmdNumber = -1;
        if (isClassUnsolicited(code) == false) {
            if (parsed.length < 3) {
                throw new IllegalArgumentException("Insufficient arguemnts");
            }
            try {
                cmdNumber = Integer.parseInt(parsed[1]);
                skiplength += parsed[1].length() + 1;
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("problem parsing cmdNumber", e);
            }
        }

        String logMessage = rawEvent;
        if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
            skiplength += parsed[2].length() + 1;
            logMessage = parsed[0] + " " + parsed[1] + " {}";
        }

        final String message = rawEvent.substring(skiplength);

        return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
    }

unmount流程framework层流程分析:

@Override
    public void unmount(String volId) {
        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
        waitForReady();
        //查找卷信息
        final VolumeInfo vol = findVolumeByIdOrThrow(volId);

        //通知pms卷设备变化信息
        if (vol.isPrimaryPhysical()) {
            final long ident = Binder.clearCallingIdentity();
            try {
                synchronized (mUnmountLock) {
                    mUnmountSignal = new CountDownLatch(1);
                    mPms.updateExternalMediaStatus(false, true);
                    waitForLatch(mUnmountSignal, "mUnmountSignal");
                    mUnmountSignal = null;
                }
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        try {
        //连接socket执行unmount命令
            mConnector.execute("volume", "unmount", vol.id);
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }
    
    public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
            throws NativeDaemonConnectorException {
        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
        }

        final long startTime = SystemClock.elapsedRealtime();

        final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();

        final StringBuilder rawBuilder = new StringBuilder();
        final StringBuilder logBuilder = new StringBuilder();
        final int sequenceNumber = mSequenceNumber.incrementAndGet();

        makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);

        final String rawCmd = rawBuilder.toString();
        final String logCmd = logBuilder.toString();

        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);
                }
            }
        }
        ......

流程图

流程图

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值