[ Android ] Android 4.4 KitKat 針對 SD 卡存取權限的新改變

Title

迫不及待想升級到 Android 4.4 KitKat 嗎?注意了!Android 4.4 的一項新變動可能會讓你面臨意想不到的麻煩…那就針對外部儲存裝置的新存取權限!


外部儲存裝置(如 SD 卡)寫入權限受到嚴格限制

最近這個話題吵的火熱啊!但是 Android 4.4 KitKat 也不是最近才出來的,怎麼會現在才熱起來呢?也許是因為率先搭載的 Nexus 系列並沒有 SD 卡的關係吧(不過還是有 Google Play Edition 的 new HTC One 跟 Galaxy S4 與 Xperia Z Ultra 啊?)。結果現在各大廠開始推送 KitKat 之後,哀嚎遍野,因為太多裝置都有外接 SD 卡,依賴 SD 卡儲存內容啊!所以問題就爆發了。

這個話題主要是 Android 4.4 KitKat 針對「外部儲存區」新的存取權限設定。

使用 Android 4 裝置的朋友們如果有使用檔案管理 app(例如 Explorer 或 Solid Explorer 或 ES File Explorer File Manager 等)會發現內建的儲存區被模擬成 sdcard0,而能夠插卡的裝置其 SD 卡會被掛載在 sdcard1 上。有 root 的朋友應該更容易感受到。在 recovery 要刷放在 SD 卡上的 zip 時,不能找 sdcard 而是要找 sdcard1 才行。這是因為在 Android 4 的時候,會將內部儲存區用來供使用者存放檔案的路徑,藉由模擬層模擬成外部儲存區。

好,這是一個很重要的觀念,先緊緊記住,接下來解釋在 Android 4.4 KitKat 上面發生了什麼事情。

根據 External Storage Technical Information 這份官方技術文件中顯示:

External Storage Technical Information

Android supports devices with external storage, which is defined to be a case-insensitive filesystem with immutable POSIX permission classes and modes. External storage can be provided by physical media (such as an SD card), or by exposing a portion of internal storage through an emulation layer. Devices may contain multiple instances of external storage.

Access to external storage is protected by various Android permissions. Starting in Android 1.0, write access is protected with the WRITE_EXTERNAL_STORAGE permission. Starting in Android 4.1, read access is protected with the READ_EXTERNAL_STORAGE permission.

Starting in Android 4.4, the owner, group and modes of files on external storage devices are now synthesized based on directory structure. This enables apps to manage their package-specific directories on external storage without requiring they hold the broad WRITE_EXTERNAL_STORAGE permission. For example, the app with package name com.example.foo can now freely access Android/data/com.example.foo/ on external storage devices with no permissions. These synthesized permissions are accomplished by wrapping raw storage devices in a FUSE daemon.

Since external storage offers minimal protection for stored data, system code should not store sensitive data on external storage. Specifically, configuration and log files should only be stored on internal storage where they can be effectively protected.

 

Multiple external storage devices


Starting in Android 4.4, multiple external storage devices are surfaced to developers throughContext.getExternalFilesDirs(), Context.getExternalCacheDirs(), and Context.getObbDirs().

External storage devices surfaced through these APIs must be a semi-permanent part of the device (such as an SD card slot in a battery compartment). Developers expect data stored in these locations to be available over long periods of time. For this reason, transient storage devices (such as USB mass storage drives) should not be surfaced through these APIs.

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

翻譯成中文看看:

外部儲存區技術資訊

Android 支援外部儲存區。Android 定義外部儲存區是一個「具備不變的 POSIX 權限類別與模式,不分大小寫的檔案系統」。外部儲存區可以是實體裝置(例如一張 SD 卡),或者是內部儲存區的一部份,透過模擬層來模擬。一部 Android 裝置可能包含數個外部儲存區。

外部儲存區的存取受到數個 Android 權限的保護。從 Android 1.0 起,寫入存取被 WRITE_EXTERNAL_STORAGE 權限所限制。從 Android 4.1 開始,讀出存取受到 READ_EXTERNAL_STORAGE 權限所限制。

從 Android 4.4 開始,放置於外部儲存裝置的檔案的擁有者(owner)、所屬群組(group)與模式將與目錄結構結合。這可以讓 app 管理在外部儲存區的專屬目錄時不需要取得 WRITE_EXTERNAL_STORAGE 權限。舉個例子,一個名為 com.example.foo 的 app 可以自由存取放在外部儲存裝置中的 Android/data/com.example.foo/ 這個目錄而不需要任何權限。這種結合權限機制是藉由利用 FUSE(Filesystem in userspace)管理原始儲存裝置來達成的。

既然外部儲存區對於儲存的資料只能提供最小的保護,系統運作上就不應該把敏感資訊儲存在外部儲存區。特別是設定與記錄檔都應該儲存在內部儲存區,這樣才能獲得有效的保護。

多重外部儲存裝置


從 Android 4.4 開始,Context.getExternalFileDirs()、Context.getExternalCacheDirs() 與 Context.getObbDirs() 讓開發人員可以處理多個外部儲存區。

可以利用這些 API 處理的外部儲存裝置必須是 Android 裝置的半常駐元件(例如在電池槽的 SD 卡插槽),開發者預期資料將會存放在那裡很長一段時間。為此,暫時性的儲存裝置(例如 USB 隨身碟)就不應該用這些 API 處理。

在 Android 裝置上,只有主要外部儲存區可以取得 WRITE_EXTERNAL_STROAGE 權限。app 除了自己的專屬目錄以外,不允許寫入次要外部儲存區。藉由這種限制寫入的方式,系統得以在移除 app 時將外部儲存區的相關檔案一併清除。


重點整理

簡單來説,Android 4.4 開始限制只有「主要外部儲存區」可以使用 WRITE_EXTERNAL_STORAGE 權限寫入檔案,但是「次要外部儲存區」除了 app 專屬路徑以外都無法寫入。

主要外部儲存區就是掛載於 sdcard 或 sdcard0 的儲存區,而掛載在 sdcard1(以及更大的數字)的就是次要外部儲存區。

好,這下精彩了。一開始有提過,從 Android 4 開始,內建儲存區供使用者存取的路徑被透過模擬層當作外部儲存區掛載,所以是理所當然的主要外部儲存區。而可以插卡的機種,SD 卡都會被當作是次要外部儲存區。

於是從 Android 4.4 開始,檔案管理 app 再也無法把檔案搬到 SD 卡給它使用的專屬目錄以外的地方了!甚至某些會寫入檔案的 app,例如可以選擇儲存路徑的拍照 app,也沒辦法再把相片儲存路徑指定到 SD 卡上任意目錄了。

雖然 XDA 有人寫出了可以暫時解決這個辦法的 app,但是需要 root 權限才能執行。對於不想 root 的人,就只能學習適應它了。


Google 為什麼要這麼做?

我不知道。但是根據技術文件看來,Google 的目標可能想解決長久以來許多人詬病的問題 ► SD 卡上常常留下 app 活動的「痕跡」,卻不會隨著 app 被移除而消失。

應該很多人都體驗過:安裝了一個 app 後,這個 app 就為了自己方便在 SD 卡上建立了一些目錄,放置了一些檔案。但是移除該 app 後,這些目錄或檔案卻不會跟著被移除,就這樣一直留在 SD 卡上。

Android 4.4 的新措施,app 在 SD 卡上只能存取限定的目錄。無法任意建立目錄通常也表示 SD 卡的目錄列表會比較整潔。而且所有檔案的存放路徑有了統一的規則。一旦 app 被反安裝,該目錄也會被系統直接移除。這樣就不留痕跡了。

但是如果 app 可以產生內容,而且你指定儲存在 SD 卡上(例如拍照 app),移除前記得先把檔案備份出來啊!否則移除就沒啦!

這一點真的是很大的問題。是所有用戶都會知道這件事情嗎?如果不知道,刪除的檔案有可能是重要的內容,這樣的損失絕對是很糟糕的使用者體驗。強制將產生的內容儲存在內部儲存區絕對不是好的解決方案,有些機種的內建儲存區很小,使用者十分依賴 SD 卡的外部儲存區來儲存內容。這種設計可能會引起使用者的厭惡。


我 root 了,我想要解決方案。

試試看來自 XDA 這篇 [APP][4.4][ROOT] SDFix: Modify device permissions to allow apps to write to MicroSD 的 app SDFix 吧。