iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
自我挑戰組

安豬複習系列 第 27

#26

  • 分享至 

  • xImage
  •  

StorageManagerService:檔案系統、App Sandbox 與權限管理

intro

在 Android 系統中,每個應用程式都會需要儲存資料:

  • App 的設定檔(SharedPreferences)
  • 暫存檔案(Cache)
  • 使用者檔案(圖片、影片、下載內容)
    然而,若讓所有 App 直接操作檔案系統,會導致嚴重的安全問題。
    為此,Android 採用了「應用沙箱化(Application Sandbox)」的機制,確保每個 App 都在獨立的檔案系統空間中運行。
    而負責協調這些儲存空間、掛載邏輯與權限控制的核心系統服務,就是 StorageManagerService(SMS)。

StorageManagerService 在系統中的位置與角色

StorageManagerService 是 SystemServer 啟動的核心服務之一,位於 Android Framework 層,主要負責:

  • 管理所有儲存設備(如內部儲存、SD 卡、USB)
  • 控制應用的檔案訪問權限(App Sandbox)
  • 掛載與卸載 Volume(透過 vold)
  • 提供 StorageStats、DiskStats、App-specific 資訊給其他服務
  • 與 MountService / vold / SELinux 整合,實現完整安全機制
+-------------------------------------------------------------+
|                     Android Framework 層                    |
|-------------------------------------------------------------|
|  App Process  |   MediaProvider   |   SystemUI   | Settings |
|-------------------------------------------------------------|
|                   StorageManagerService (SMS)               |
|           ↑              ↑              ↑                   |
|           │              │              │                   |
|     MountService     StorageStats    StorageVolume API      |
+-------------------------------------------------------------+
|                     Native 層 (vold, sdcardd)               |
|     vold ↔ libstoragemanager ↔ kernel (ext4, f2fs, fuse)    |
+-------------------------------------------------------------+
|                     Linux Kernel 層 (VFS)                   |
|           /data, /sdcard, /mnt/runtime/default              |
+-------------------------------------------------------------+

StorageManagerService 啟動流程

(1) 啟動時機
SystemServer.java 在開機階段會啟動 StorageManagerService:

traceBeginAndSlog("StartStorageManagerService");
mSystemServiceManager.startService(StorageManagerService.class);
traceEnd();

(2) 初始化階段
在 StorageManagerService.java 的 onStart() 方法中:

  • 建立與 vold(Volume Daemon) 的 IPC 通道。
  • 初始化各種 Volume 資訊。
  • 註冊廣播(如 SD 卡插拔事件)。
  • 建立 Volume ID 與 UUID 映射表。

Android 儲存架構總覽

Android 採用「多層式儲存抽象架構」,可分為三大類:

類型 路徑範例 說明
內部儲存 (Internal Storage) /data/data/<package> 每個 App 獨立空間(沙箱)
外部儲存 (External Storage) /storage/emulated/0/ 共用空間(照片、影片、下載)
可移除儲存 (Removable Storage) /storage/XXXX-XXXX SD 卡或 USB 裝置

App Sandbox 機制

Android 的 App 沙箱機制是基於 Linux User ID (UID) 的安全隔離模型:

  • 每個 App 都擁有獨立 UID(例如:u0_a101)
  • 對應的私有目錄為 /data/data/com.example.app/
  • 只有該 UID 的進程可讀寫該目錄
+---------------------------------------------+
| App A (UID=10056) -> /data/data/com.appA/   |
|   ├── shared_prefs/                         |
|   ├── cache/                                |
|   └── databases/                            |
+---------------------------------------------+
| App B (UID=10057) -> /data/data/com.appB/   |
|   ├── files/                                |
|   ├── cache/                                |
|   └── lib/                                  |
+---------------------------------------------+
   ↑ 無法直接跨訪問

系統層面透過 SELinux Policy 與 Mount Namespace 來防止跨應用存取。

Volume 與 Mount 機制

StorageManagerService 與底層的 vold(Volume Daemon)協作,管理實際的磁碟掛載。

Volume 類型 主要種類說明
Public Volume 可移除媒體,如 SD 卡、USB
Private Volume App 專屬儲存空間,採加密方式存放
Emulated Volume 模擬外部儲存(常見於 /storage/emulated/0)

掛載流程

[SD 卡插入]
     ↓
kernel uevent
     ↓
vold 接收事件並建立 Volume 物件
     ↓
StorageManagerService 通知系統 (mountVolume)
     ↓
BroadcastReceiver 廣播 ACTION_MEDIA_MOUNTED
     ↓
MediaProvider 重新掃描媒體檔案

Scoped Storage(分區儲存)機制

自 Android 10 (API 29) 起,Google 引入 Scoped Storage,限制 App 對外部儲存的訪問範圍。
改變前後對比:

版本 存取模式 範圍
Android 9 以前 WRITE_EXTERNAL_STORAGE 可自由讀寫 /sdcard/
Android 10 以後 Scoped Storage 僅可訪問 App 專屬目錄或授權檔案

Scoped Storage 的導入讓 StorageManagerService 的職責更重,因為它需協調:

  • 每個 App 的沙箱目錄(/Android/data/)
  • 媒體共享 URI(MediaStore)
  • 臨時存取授權(MediaStore.createWriteRequest())

Storage Access Framework (SAF)

SAF 是 Android 為解決使用者檔案選取而設計的高階 API。
底層仍由 StorageManagerService 協助管理 URI 存取權限。

Ex.

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = "image/*"
}
startActivityForResult(intent, PICK_IMAGE)

使用者選取檔案後,App 會得到一個 content:// URI,而非實際檔案路徑。
該 URI 背後由 DocumentsProvider 與 StorageManagerService 協調管理,確保安全。

權限管理與隔離策略

Android 的檔案權限系統綜合了以下三層防護:

層級 技術 說明
Kernel 層 UID/GID 權限位元 以 Linux 檔案權限模式 (rwx) 控制存取
Framework 層 StorageManagerService + AppOps 判斷呼叫者權限
應用層 Scoped Storage、FileProvider 控制授權的 URI

此外,StorageManagerService 也負責:

  • 管理 App 對儲存的授權(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE)
  • 處理 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO 權限
  • 通知 MediaProvider 當資料變化

App 檔案路徑舉例

資料類型 路徑 存取方式
內部私有資料 /data/data// 僅該 App 可用
外部私有資料 /storage/emulated/0/Android/data// 需權限
共用媒體資料 /storage/emulated/0/DCIM、Pictures 經由 MediaStore 存取
暫存檔案 /cache/ 系統可清除

StorageManagerService 的主要 API

API 功能
getVolumes() 取得所有儲存裝置資訊
getUuidForPath() 查詢 Volume UUID
getAllocatableBytes() 取得可用空間
mount() / unmount() 掛載或卸載 Volume
registerListener() 註冊磁碟事件監聽
setPrimaryStorageUuid() 指定主要儲存區

Ex.

val sm = context.getSystemService(StorageManager::class.java)
val volumes = sm.storageVolumes
volumes.forEach {
    Log.d("Volume", "desc=${it.getDescription(context)} path=${it.directory}")
}

儲存空間統計與清理

StorageManagerService 也負責統計各 App 的磁碟用量,供設定頁面「儲存空間」顯示。

Settings → Storage → App usage

實作上透過:

  • StorageStatsManager
  • StorageStatsService(由 SMS 呼叫)
  • /proc/uid_stat、Quota 支援(EXT4/F2FS)

Android 會依據用量進行清理(如自動刪除 Cache),確保儲存空間健康。

整體資料流與權限控制

+--------------------+       +------------------------+
| App (com.appA)     |       | App (com.appB)         |
|   UID=10055        |       |   UID=10056            |
|  request file      |       |                        |
+---------+----------+       +-----------+------------+
          |                              |
          v                              v
     [StorageManagerService]   ←───→   [AppOpsManager]
          │                              │
          v                              v
       [vold / sdcardd]     ←──→    [Kernel / SELinux]
          │
          v
   /data, /mnt, /storage/emulated/0

best practice

  • 使用 App 專屬儲存目錄
    • 使用 getExternalFilesDir() 或 getFilesDir()
    • 避免直接操作 /sdcard/
  • 使用 MediaStore 代替 File API
    • 更安全,支援 Scoped Storage 模式
  • 用 SAF 進行使用者授權的檔案選取
    • 使用 ACTION_OPEN_DOCUMENT 取代傳統 File Picker
  • 避免在主執行緒操作 I/O
    • 改用 Coroutine, ExecutorService 或 WorkManager

in thefuture

Android 的儲存架構逐漸朝向:

  • 零共享原則:每個 App 僅能訪問必要資料
  • User consent first:所有跨應用存取需經使用者同意
  • 更強的加密與分層存取(例如:Android 15 引入 per-user encryption)
  • 融合雲端儲存(StorageManagerService 將與 Drive/Photos 同步框架整合)

summary

關鍵點 說明
核心職責 管理儲存裝置、App 沙箱與存取權限
系統位置 Framework 層的 System Service
關聯元件 vold、MediaProvider、AppOpsManager
主要挑戰 平衡安全性、可用性與相容性
演進趨勢 Scoped Storage、SAF、使用者授權化
        +-------------------------------------------+
        |               App Layer                   |
        |   (File API, MediaStore, SAF)             |
        +--------------------▲----------------------+
                             │
        +--------------------│----------------------+
        |        Framework Layer                    |
        |  StorageManagerService / AppOpsManager     |
        +--------------------▲----------------------+
                             │
        +--------------------│----------------------+
        |      Native Layer  (vold, libstoragemgr)  |
        +--------------------▲----------------------+
                             │
        +--------------------│----------------------+
        |    Kernel Layer (VFS, ext4, FUSE, SELinux)|
        +-------------------------------------------+

上一篇
#25
下一篇
#27
系列文
安豬複習31
  1. 27
    #26
  2. 28
    #27
  3. 29
    #28
  4. 30
    #29
  5. 31
    #30
完整目錄
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言