Android存儲系統(tǒng)源碼走讀(一):StorageManagerService

前言

Android外部存儲空間由 vold init 服務(wù)和 StorageManagerService 系統(tǒng)服務(wù)共同管理肋僧。外部實(shí)體存儲卷的裝載由 vold 處理:通過執(zhí)行分階段操作準(zhǔn)備好媒體剿涮,然后再將其提供給應(yīng)用萍悴。
從 Android 8.0 開始入篮,MountService 類已更名為 StorageManagerService谨垃。
并且StorageManagerService與Vold的通信方式由socket方式變?yōu)閎inder方式,精簡了部分代碼墓拜。

涉及代碼路徑:

基于android11走讀存儲系統(tǒng)部分代碼
frameworks/base/services/core/java/com/android/server/StorageManagerService.java
frameworks/base/services/java/com/android/server/SystemServer.java

StorageManagerService

startService

StorageManagerService同其他系統(tǒng)服務(wù)一樣港柜,也是從SystemServer啟動

    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咳榜。
private static final String STORAGE_MANAGER_SERVICE_CLASS = "com.android.server.StorageManagerService$Lifecycle";

SystemManagerService通過反射構(gòu)建LifeCycle并調(diào)用onStart()方法

        @Override
        public void onStart() {
            mStorageManagerService = new StorageManagerService(getContext());
            publishBinderService("mount", mStorageManagerService);
            mStorageManagerService.start();
        }

StorageManagerService

    public StorageManagerService(Context context) {
        sSelf = this;

        // 前面先是讀取一些屬性狀態(tài)夏醉,其中關(guān)于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);
        // 創(chuàng)建名為“StorageManagerService”的線程,并創(chuàng)建對應(yīng)的Handler
        HandlerThread hthread = new HandlerThread(TAG);
        hthread.start();
        mHandler = new StorageManagerServiceHandler(hthread.getLooper());
        // mObbActionHandler對應(yīng)“android.io”線程
        // Add OBB Action Handler to StorageManagerService thread.
        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());

        mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);

        // 啟動installd服務(wù)
        mInstaller = new Installer(mContext);
        mInstaller.onStart();

        // Initialize the last-fstrim tracking if necessary
        File dataDir = Environment.getDataDirectory();
        File systemDir = new File(dataDir, "system");
        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
        //判斷/data/system/last-fstrim文件涌韩,不存在則創(chuàng)建畔柔,存在則更新最后修改時間
        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);

        // 監(jiān)聽ACTION_USER_ADDED、ACTION_USER_REMOVED廣播
        final IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_ADDED);
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

        // 內(nèi)部私有volume的路徑為/data臣樱,該volume通過dumpsys mount是不會顯示的
        synchronized (mLock) {
            addInternalVolumeLocked();
        }

        // Add ourself to the Watchdog monitors if enabled.
        if (WATCHDOG_ENABLE) {
            Watchdog.getInstance().addMonitor(this);
        }

        // 汽車應(yīng)用支持
        mIsAutomotive = context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_AUTOMOTIVE);
    }

FUSE

FUSE(Filesystem in Userspace)

為了實(shí)現(xiàn)更靈活的權(quán)限管理能力靶擦,Android引入了fuse文件系統(tǒng)。

Filesystem in Userspace顧名思義雇毫,即在用戶空間的文件系統(tǒng)奢啥。
為什么要強(qiáng)調(diào)用戶空間呢?接觸過Linux內(nèi)核的同學(xué)大概會知道嘴拢,文件系統(tǒng)一般是實(shí)現(xiàn)在內(nèi)核里面的,比如寂纪,Ext4席吴、Fat32赌结、NTFS(Kernel原生版)等常見的文件系統(tǒng),其代碼都在內(nèi)核中孝冒,而FUSE特殊之處就是柬姚,其文件系統(tǒng)的核心邏輯是在用戶空間實(shí)現(xiàn)的。

FUSE實(shí)現(xiàn)原理

image.png

圖中體現(xiàn)了FUSE的2個關(guān)鍵部分(綠色方框)庄涡,分別是Kernel中的那個FUSE(這里簡稱kernel FUSE)和user space中的那個fuse_user程序量承。其中kernel FUSE是負(fù)責(zé)把從用戶層過來的文件系統(tǒng)操作請求傳遞給fuse_user程序的,而這個fuse_user程序?qū)崿F(xiàn)了前面所說的文件系統(tǒng)的核心邏輯穴店。

onStart

    private void start() {
        connectStoraged();
        connectVold();
    }
    
    private void connectStoraged() {
        IBinder binder = ServiceManager.getService("storaged");
        if (binder != null) {
            try {
                // 設(shè)置死亡代理以重新建立連接
                binder.linkToDeath(new DeathRecipient() {
                    @Override
                    public void binderDied() {
                        Slog.w(TAG, "storaged died; reconnecting");
                        mStoraged = null;
                        connectStoraged();
                    }
                }, 0);
            } catch (RemoteException e) {
                binder = null;
            }
        }

        if (binder != null) {
            // 獲取storaged的bp端用于通信
            mStoraged = IStoraged.Stub.asInterface(binder);
        } else {
            Slog.w(TAG, "storaged not found; trying again");
        }

        if (mStoraged == null) {
            BackgroundThread.getHandler().postDelayed(() -> {
                connectStoraged();
            }, DateUtils.SECOND_IN_MILLIS);
        } else {
            onDaemonConnected();
        }
    }

    private void connectVold() {
        IBinder binder = ServiceManager.getService("vold");
        if (binder != null) {
            try {
                // 設(shè)置死亡代理以重新建立連接
                binder.linkToDeath(new DeathRecipient() {
                    @Override
                    public void binderDied() {
                        Slog.w(TAG, "vold died; reconnecting");
                        mVold = null;
                        connectVold();
                    }
                }, 0);
            } catch (RemoteException e) {
                binder = null;
            }
        }

        if (binder != null) {
            // 獲取Vold的bp端用于通信
            mVold = IVold.Stub.asInterface(binder);
            try {
                // 關(guān)鍵代碼:設(shè)置Vold的Listener
                mVold.setListener(mListener);
            } catch (RemoteException e) {
                mVold = null;
                Slog.w(TAG, "vold listener rejected; trying again", e);
            }
        } else {
            Slog.w(TAG, "vold not found; trying again");
        }

        if (mVold == null) {
            BackgroundThread.getHandler().postDelayed(() -> {
                connectVold();
            }, DateUtils.SECOND_IN_MILLIS);
        } else {
            onDaemonConnected();
        }
    }

connectStoraged和connectVold分別是獲取Vold和Storaged服務(wù)的bp端撕捍,設(shè)置死亡代理,
為Vold設(shè)置Listener監(jiān)聽泣洞,然后調(diào)用onDaemonConnected

    public void onDaemonConnected() {
        mDaemonConnected = true;
        mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
    }
    
    private void handleDaemonConnected() {
        initIfBootedAndConnected();
        resetIfBootedAndConnected();

        // On an encrypted device we can't see system properties yet, so pull
        // the system locale out of the mount service.
        if ("".equals(VoldProperties.encrypt_progress().orElse(""))) {
            copyLocaleFromMountService();
        }
    }
    
        private void initIfBootedAndConnected() {
        Slog.d(TAG, "Thinking about init, mBootCompleted=" + mBootCompleted
                + ", mDaemonConnected=" + mDaemonConnected);
        if (mBootCompleted && mDaemonConnected
                && !StorageManager.isFileEncryptedNativeOnly()) {
            // 根據(jù)persist.sys.emulate_fbe確定用戶目錄的加鎖/解鎖狀態(tài)
            final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly();
            Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked);
            final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
            for (UserInfo user : users) {
                try {
                    if (initLocked) {
                        mVold.lockUserKey(user.id);
                    } else {
                        mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null),
                                encodeBytes(null));
                    }
                } catch (Exception e) {
                    Slog.wtf(TAG, e);
                }
            }
        }
    }

    private void resetIfBootedAndConnected() {
        Slog.d(TAG, "Thinking about reset, mBootCompleted=" + mBootCompleted
                + ", mDaemonConnected=" + mDaemonConnected);
        // systemserver啟動進(jìn)入bootcomplete階段 
        if (mBootCompleted && mDaemonConnected) {
            final UserManager userManager = mContext.getSystemService(UserManager.class);
            final List<UserInfo> users = userManager.getUsers();

            if (mIsFuseEnabled) {
                mStorageSessionController.onReset(mVold, () -> {
                    mHandler.removeCallbacksAndMessages(null);
                });
            } else {
                killMediaProvider(users);
            }

            final int[] systemUnlockedUsers;
            synchronized (mLock) {
                // make copy as sorting can change order
                systemUnlockedUsers = Arrays.copyOf(mSystemUnlockedUsers,
                        mSystemUnlockedUsers.length);
                // 清空mDisk和mVolumes兩個ArrayMap
                mDisks.clear();
                mVolumes.clear();
                // 將/data為路徑的private volume添加到mVolumes
                addInternalVolumeLocked();
            }

            try {
                // 通過Vold的bp端調(diào)用reset()方法
                // TODO(b/135341433): Remove paranoid logging when FUSE is stable
                Slog.i(TAG, "Resetting vold...");
                mVold.reset();
                Slog.i(TAG, "Reset vold");

                // 通知Vold所有的用戶和已解鎖用戶
                for (UserInfo user : users) {
                    mVold.onUserAdded(user.id, user.serialNumber);
                }
                for (int userId : systemUnlockedUsers) {
                    mVold.onUserStarted(userId);
                    mStoraged.onUserStarted(userId);
                }
                if (mIsAutomotive) {
                    restoreAllUnlockedUsers(userManager, users, systemUnlockedUsers);
                }
                mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
                mStorageManagerInternal.onReset(mVold);
            } catch (Exception e) {
                Slog.wtf(TAG, e);
            }
        }
    }

onBootPhase

        @Override();
        public void onBootPhase(int phase) {
            if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
                mStorageManagerService.servicesReady();
            } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mStorageManagerService.systemReady();
            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                mStorageManagerService.bootCompleted();
            }
        }

SystemServiceManager的startBootPhase()貫穿system_server進(jìn)程的整個啟動過程

image.png

1. servicesReady

    private void servicesReady() {
        mPmInternal = LocalServices.getService(PackageManagerInternal.class);

        mIPackageManager = IPackageManager.Stub.asInterface(
                ServiceManager.getService("package"));
        mIAppOpsService = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        
        // MediaProvider
        ProviderInfo provider = getProviderInfo(MediaStore.AUTHORITY);
        if (provider != null) {
            mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
            sMediaStoreAuthorityProcessName = provider.applicationInfo.processName;
        }

        // DownloadsProvider
        provider = getProviderInfo(Downloads.Impl.AUTHORITY);
        if (provider != null) {
            mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
        }

        // DocumentsUiProvider
        provider = getProviderInfo(DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY);
        if (provider != null) {
            mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid);
        }

        if (!mIsFuseEnabled) {
            try {
                mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null,
                        mAppOpsCallback);
                mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback);
            } catch (RemoteException e) {
            }
        }
    }

2. systemReady

    private void systemReady() {();
        LocalServices.getService(ActivityTaskManagerInternal.class)
                .registerScreenObserver(this);

        mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
    }
    
    private void handleSystemReady() {
        //計劃執(zhí)行日常的fstrim操作?
        // Start scheduling nominally-daily fstrim operations
        MountServiceIdler.scheduleIdlePass(mContext);

        // 響應(yīng)設(shè)置而切換zram-enable系統(tǒng)屬性
        mContext.getContentResolver().registerContentObserver(
            Settings.Global.getUriFor(Settings.Global.ZRAM_ENABLED),
            false /*notifyForDescendants*/,
            new ContentObserver(null /* current thread */) {
                @Override
                public void onChange(boolean selfChange) {
                    refreshZramSettings();
                }
            });
        refreshZramSettings();

        // 計劃zram寫回忧风,除非由persist.sys.zram_enabled禁用了zram
        String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
        if (!zramPropValue.equals("0")
                && mContext.getResources().getBoolean(
                    com.android.internal.R.bool.config_zramWriteback)) {
            ZramWriteback.scheduleZramWriteback(mContext);
        }
        // 響應(yīng)設(shè)置切換isolated-enable 的系統(tǒng)屬性
        mContext.getContentResolver().registerContentObserver(
            Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE),
            false /*notifyForDescendants*/,
            new ContentObserver(null /* current thread */) {
                @Override
                public void onChange(boolean selfChange) {
                    refreshIsolatedStorageSettings();
                }
            });
            // 在屬性更改時克隆屬性
            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                mContext.getMainExecutor(), (properties) -> {
                    refreshIsolatedStorageSettings();
                    refreshFuseSettings();
                });
        refreshIsolatedStorageSettings();
    }

3. bootCompleted

    private void bootCompleted() {
        mBootCompleted = true;
        mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget();
        updateFusePropFromSettings();
    }
    
    private void handleBootCompleted() {
        // 與onStart部分調(diào)用一致
        initIfBootedAndConnected();
        resetIfBootedAndConnected();
    }

小結(jié)

StorageManagerService與storaged和Vold服務(wù)建立鏈接,并持有了storaged和Vold的BP對象以進(jìn)行通信球凰。
StorageManagerService還設(shè)置Vold設(shè)置了Listener接收Vold回調(diào)過來的各類事件狮腿。
下文我將分析Vold部分代碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呕诉,一起剝皮案震驚了整個濱河市缘厢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甩挫,老刑警劉巖贴硫,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捶闸,居然都是意外死亡夜畴,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門删壮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贪绘,“玉大人,你說我怎么就攤上這事央碟∷肮啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵亿虽,是天一觀的道長菱涤。 經(jīng)常有香客問我,道長洛勉,這世上最難降的妖魔是什么粘秆? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮收毫,結(jié)果婚禮上攻走,老公的妹妹穿的比我還像新娘殷勘。我一直安慰自己,他們只是感情好昔搂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布玲销。 她就那樣靜靜地躺著,像睡著了一般摘符。 火紅的嫁衣襯著肌膚如雪贤斜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天逛裤,我揣著相機(jī)與錄音瘩绒,去河邊找鬼。 笑死别凹,一個胖子當(dāng)著我的面吹牛草讶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炉菲,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼堕战,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拍霜?” 一聲冷哼從身側(cè)響起嘱丢,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎祠饺,沒想到半個月后越驻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡道偷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年缀旁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勺鸦。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡并巍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出换途,到底是詐尸還是另有隱情懊渡,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布军拟,位于F島的核電站剃执,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏懈息。R本人自食惡果不足惜肾档,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辫继。 院中可真熱鬧阁最,春花似錦戒祠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽低千。三九已至配阵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間示血,已是汗流浹背棋傍。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留难审,地道東北人瘫拣。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像告喊,于是被迫代替她去往敵國和親麸拄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容