不知道你有麼有發現,來自菜鳥的成長史:http://blog.csdn.net/zjbpku/article/details/25161131,html
KitKat以後的版本再也不支持用戶對外置SDcard(Secondary Storage)的寫入等操做。若是用戶想要將文件等copy到手機中,則只能java
存儲到內部存儲器中,而沒法存儲到外置sdcard中,並且沒法建立新的文件夾,這樣一來給用戶和開發者都帶來了必定的不便。之所android
以在KitKat以後版本中沒法操做外置Sdcard,是由於Google更改了此模塊的權限,之前咱們能夠直接獲取WRITE_EXTERNAL_STORAGEshell
和READ_EXTERNAL_STORAGE權限來直接操做Sdcard,如今則不能,其目的是軟件卸載時能將該軟件建立的文件所有刪除。據Googleapi
員工Jeff SharKey(此模塊的開發者)介紹,自Kitkat以後Anroid提供了新的API去訪問Secondary External Storage,但這不是本文重點,本app
文重點是分析外部存儲權限是如何做用的。框架
在KitKat以前的Android版本會給應用程序單獨分出一塊外部存儲空間(external storage),這塊存儲空間可能在sdcardide
(可插拔的外置sdcaard)上,也可能在僅僅是在設備內部的閃存上,咱們要得到WRITE_EXTERNAL_STORAGE權限在能對這塊工具
空間進行訪問,若是隻是讀取內容則不須要權限。在4.4 KitKat及以後的版本中,Google作了兩個變化:一、進行讀取時須要ui
READ_EXTERNAL_STORAGE權限;二、訪問應用所屬的目錄下(如:android/data/[package name])存儲的數據是不須要任
何權限的。
KitKat中,外部存儲(external storage)被分割成了多個部分:一個「primary」部分,一個或多個「secondary」部分。在Kitkat以前的
API 能夠用來操做 primary external storage,secondary external storage 是對write權限作了稍微修改,與上邊所述同樣,在應用所
所屬的目錄(如:android/data/[package name])下,對文件是有全部操做權限的,在應用所能管理到目錄以外,該應用則不具備寫
的權限,不能進行任何寫的操做。這裏也就引出了本文的重點。ps:Google雖然沒有要求各廠商在Sdcard的操做上添加額外權限,但
是它卻強制要求製造商對secondary external storage作了權限限制。若是你對Internal storage和external storage有疑問,能夠看看文
檔 https://developer.android.com/guide/topics/data/data-storage.html#filesInternal
根據Jeff SharKey 的介紹,當前版本的Android系統,也就是Kitkat,使用FUSE (Filesysgem in Userspace ) 對external storage進
行管理。爲了在文件建立時獲取必要的權限,動態地接受或拒絕來自用戶/組的個別請求,會有一個Android 守護進程參與與FUSE 內核
驅動的交互。這僅僅是Android在FAT File System 格式化後的可移動捲上使用Linux型權限的一部分,在內核中它也容許使用超出基本的
owner/gouper/user 執行的多級權限控制。看看下面Jeff Sharkey的解釋:
https://android.googlesource.com/platform/system/core/+/master/sdcard/sdcard.c
在4.4以前,framework api對存儲卷(storage volumes)的操做並無很大的改變,設備製造商能夠建立單個「primary」卷或者多個「secondary」
卷,而這些不一樣的卷都能被系統服務StorageManager和MountService管理,這中狀況下訪問「primary」部分就像訪問單個external storage同樣。
不少設備有Sd卡,可是都沒有把它看成external storage,實際上這就是這些設備的「secondary volume」。例如,三星的Galaxy系列就是屬於這
一類,從權限方面來講,sd卡其實像外部存儲卷同樣被管理,可是做爲設備的「secondary external storage",是沒有API能夠進行寫的操做的。
下面的這段代碼來自AOSP device storage conf iguration example:
[java] view plaincopyprint?
on init
mkdir /mnt/shell/emulated 0700 shell shell
mkdir /storage/emulated 0555 root root
mkdir /mnt/media_rw/sdcard1 0700 media_rw media_rw
mkdir /storage/sdcard1 0700 root root
export EXTERNAL_STORAGE /storage/emulated/legacy
export EMULATED_STORAGE_SOURCE /mnt/shell/emulated
export EMULATED_STORAGE_TARGET /storage/emulated
export SECONDARY_STORAGE /storage/sdcard1
系統內部的應用能夠訪問secondary storage的任何部分,對於第三方應用幾乎不可能(目前ES FileExplore、Airdroid、Fx等幾個文件應用經過
特別的解決方法能夠實現對某些機型外部存儲文件的操做)。(關於如何在4.4上操做文件能夠參考Storage Options。自4.4開始,Google引入
SAF框架(Storage Access Framework),若是Google之後不改變如今對4.4系統外置sd的操做權限,對於開發者而言,熟悉SAF框架也許是必要的。
另,在4.4系統內部應用中,你會發現有一個叫DocumentUI的apk,這個就是用來處理SAF的一些接口的。)
在external storage下的目錄文件擁有相同的權限,以下:
4.4 設備:
[java] view plaincopyprint?
root@generic :/storage/sdcard # ll
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Alarms
d---rwxr-x system sdcard_rw 2014-05-06 13:21 Android
d---rwxr-x system sdcard_rw 2014-05-06 13:20 DCIM
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Download
d---rwxr-x system sdcard_rw 2014-05-06 13:18 LOST.DIR
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Movies
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Music
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Notifications
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Pictures
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Podcasts
d---rwxr-x system sdcard_rw 2014-05-06 13:20 Ringtones
root@generic :/storage/sdcard # ll Android/data/
drwxrwx--- system sdcard_rw 2014-05-06 13:21 com.google.android.apps.maps
4.4 設備:
[java] view plaincopyprint?
root@generic :/storage/sdcard # ll
drwxrwx--- root sdcard_r 2013-11-27 23:35 Alarms
drwxrwx--x root sdcard_r 2013-11-27 23:36 Android
drwxrwx--- root sdcard_r 2014-05-06 01:33 DCIM
drwxrwx--- root sdcard_r 2013-11-27 23:35 Download
drwxrwx--- root sdcard_r 2013-11-28 04:33 LOST.DIR
drwxrwx--- root sdcard_r 2013-11-27 23:35 Movies
drwxrwx--- root sdcard_r 2013-11-27 23:35 Music
drwxrwx--- root sdcard_r 2013-11-27 23:35 Notifications
drwxrwx--- root sdcard_r 2013-11-27 23:35 Pictures
drwxrwx--- root sdcard_r 2013-11-27 23:35 Podcasts
drwxrwx--- root sdcard_r 2013-11-27 23:35 Ringtones
root@generic :/storage/sdcard # ll Android/data/
drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 com.google.android.apps.maps
root@generic :/storage/sdcard # ll Android/data/com.google.android.apps.maps/
drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 cache
drwxrwx--- u0_a33 sdcard_r 2013-11-27 23:36 testdata
注意:在4.3中,sdcard_rw組有所有的讀寫權限,在Kitkat中,sdcard_r 組有 +rwx 全部權限,實際上這是明顯不對的。並不等表示所有,
由於Fuse守護進程會在運行時中積極地參與修改應用的權限。這對File APIs canWrite(),canRead()和canExecute()的執行結果有很大的影
響,這些方法返回的值被單獨地記錄在內核文件系統中,因此他們都會返回true,即便試圖以POSIX打開文件也會失敗。(在4.4的外置sd
卡上,是不能在文件夾寫入一下文件的,可是當你試圖調用canWrite()方法來判斷該文件夾是否可寫時,它仍會返回true值,因此此法不可取)
android.permission.WRITE_EXTERNAL_STORAGE權限被授給sdcard_r組和sdcard_rw組的成員,但在kitkat中認證write權限須要一些動
態的檢查,所以FUSE守護進程會被用來補充文件系統的權限,FUSE守護進程會強制賦予擁有特定目錄的App每一個權限(也就是訪問自身數
據存儲的目錄android/data/pack-agename...及一些公共目錄)。對於sdcard_rw組中使用-w標誌配置的非默認全部者,FUSE守護進程也會強
制賦予write-protected權限。
[java] view plaincopyprint?
service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated
class late_start
service fuse_sdcard1 /system/bin/sdcard -u 1023 -g 1023 -w 1023 -d /mnt/media_rw/sdcard1 /storage/sdcard1
class late_start
disabled
從上面兩句程序能夠看到,FUSE守護進程強制控制GID 1023(media_rw,系統應用纔有)才能對secondar storage進行寫操做。再引入
一個問題,在4.4中將external storage 分爲primary和secondary,在primary部分(內置sdcard)是能夠進行寫操做的,而在secondary部分
(外置sdcard)是不容許的,那FUSE Daemon是如何區分控制的呢?據Jeff 解釋說: 「-w 2013" 就代表了強制使用media_rw GID才能在
secondary部分具備write權限。
下面咱們就梳理一下,若是在擁有外置sd卡的kitkat設備上進行文件操做,對於開發者而言哪些能作、哪些不能作?下圖給出開發者會嘗試
的一些操做及結果:
總結一下,自4.4開始Google對secondary volume作了限制以後,不只爲用戶帶來了不便,也爲設備製造商及開發者帶來了諸多不便,華爲
更是爲此給開發者們發了一份通告:Android4.4上應用寫外卡的兼容性問題與解決建議。現在,除了一些OEM廠商自行修改權限後的Rom對
第三方應用沒有限制外,大牛們也爲已Root的設備用戶提出修改platform.xml文件來修改權限(具體放法請百度之)以使第三方應用能夠操做
外置sd卡;還有一些上面提到的文件管理工具也能夠操做外置sd卡。無論Google作限制的初衷是什麼,但願Google從用戶的角度來考慮問題,
對Android系統作出更好的該進。在此感謝一下FX 文件管理工具的開發者Tod Liebeck 在G+對我問題的及時解答及幫助,同時也感謝一下給
Tod Liebeck解決Kitkat外置sd文件操做方案的X-plore的開發者。