1. 如何判斷android用戶拒絕了某項許可權
Android框架包含了對各種Camera以及其上可用的Camera功能的支持,它允許你在應用程序中抓拍照片和視頻。
注意事項
在開啟應用程序使用Android設備上的Camera功能之前,要考慮一些打算如何使用這些硬體功能的問題:
1. Camera需求:要考慮應用程序是否必須要運行在有Camera的設備上,如果必須,就要在應用程序的清單中聲明Camera需求;
2. 快照或定製Camera:應用程序准備如何使用Camera?是只對抓拍或視頻剪輯感興趣?還是要應用程序提供使用Camera的新方法?對於抓拍或剪輯,要考慮使用既存的Camera應用程序。對於開發定製化的Camera功能,請看下文的「構建Camera應用程序」
基礎
Android框架通過Camera API或Camera Intent來支持拍照和錄像,以下是相關的類:
Camera
這個類是控制設備Camera的主API。在構建一個Camera應用程序時,它被用於拍照或錄像。
SurfaceView
這個類用於向用戶實時的展現Camera的預覽。
MediaRecorder
這個類用於記錄來自Camera的視頻
Intent
MediaStore.ACTION_IMAGE_CAPTURE或MediaStore.ACTION_VIDEO_CAPTURE類型的Intent動作被用於不直接使用Camera對象來拍照或錄像。
清單聲明
在開始使用Camera API開發應用程序之前,要確保清單文件已經有了適當的聲明,以允許使用Camera硬體和其他相關的功能。
1. Camera許可權:應用程序必須申請使用設備Camera的許可權。
<uses-permissionandroid:name="android.permission.CAMERA"/>
注意:如果通過Intent來使用Camera,應用程序就不需要申請這個許可權。
2. Camera功能:應用程序還必須要聲明打算使用的Camera功能,例如:
<uses-featureandroid:name="android.hardware.camera"/>
把Camera功能添加到應用程序的清單中,會讓Google Play防止把程序安裝到不包含Camera或不支持你所需要的Camera功能的設備上。關於如何使用基於功能過濾的Google Play,請看Google Play和基於功能的過濾
如果應用程序能夠使用Camera或正確的操作Camera功能,但卻不需要它,那麼就應該在清單中指定android:required屬性,並把屬性值設置為false:
<uses-feature android:name="android.hardware.camera" android:required="false" />
3. 存儲許可權:如果應用程序要把圖片或視頻保存到設備的外部存儲器上(如SD卡),就必須在清單中指定這個許可權:
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
4. 音頻錄音許可權:對於視頻採集的音頻錄音,應用程序必須要申請音頻採集許可權:
<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>
5. 位置定位許可權:如果應用程序要給圖片標記GPS位置信息,就必須申請位置定位許可權:
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/>
關於獲得用戶位置的更多信息,請看「定位策略」
使用既存的Camera應用
在應用程序中不需要太多的額外代碼就可以快速的開啟拍照或錄像的方法是:使用Intent來調用一個既存的Android Camera應用程序。一個Camera Intent能夠通過既存的Camera應用程序和它返回給應用程序的播放控制來申請採集一張照片或一段視頻。本節會向你展示如何使用這項技術來採集一張圖片或一段視頻。
調用Camera Intent的過程會遵循以下這些大概的步驟:
1. 編寫一個Camera Intent:創建一個申請圖片或視頻的Intent對象,使用以下Intent類型之一:
MediaStore.ACTION_IMAGE_CAPTURE:從一個既存的Camera應用中申請圖片功能的Intent動作類型;
MediaStore.ACTION_VIDEO_CAPTURE:從一個既存的Camera應用中申請視頻功能的Intent動作類型。
2. 啟動Camera的Intent:使用startActivityForResult()方法來執行Camera的Intent。Intent啟動後,該Camera應用程序的用戶界面會顯示在屏幕上,並且用戶能夠拍照或錄像;
3. 接收Intent的結果:在你的應用程序中建立一個onActivityResult()方法來接收來自Camera Intent的回調和數據。當用戶完成成拍照或錄像(或者是取消操作),系統會調用這個方法。
圖像採集Intent
使用Camera Intent來採集圖像是你的應用程序用最少的代碼來拍照的快捷方式。一個圖片採集Intent能夠包含以下額外的信息:
MediaStore.EXTRA_OUTPUT:這個設置需要一個指定了保存圖片路徑和文件名的Uri對象。這個設置是可選,但強烈推薦使用。如果不指定這個值,Camera應用程序會用默認的名稱把採集到的圖片保存到默認的位置,這些默認值在Intent.getData()方法的返回欄位中指定。
以下示例演示了如何構建一個圖片採集Intent,並執行它。示例中GetOutputMediaFileUri()方法引用了下面「保存媒體文件」一節中的示例代碼:
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private Uri fileUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// create Intent to take a picture and return control to the calling application
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
// start the image capture Intent
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
startActivityResult()方法執行完成後,用戶就會看到Camera應用程序的界面。用戶完成拍照(或取消操作)之後,用戶界面就會返回到你的應用程序中,並且你必須監聽onActivityResult()方法來接收Intent的結果,並繼續你的應用程序的執行。
視頻採集Intent
使用Camera Intent採集視頻是讓你的應用程序能夠用最少的代碼來錄像的一中快捷方式。視頻採集Intent能夠包含以下額外信息:
MediaStore.EXTRA_OUTPUT:這個設置要求用一個URI來指定保存視頻的路徑和文件名。雖然它是可選的,但強烈推薦使用這個設置。如果沒有指定這個設置,那麼Camera應用程序會把採集到的視頻用默認的名稱保存到默認的位置,默認的設置是在Intent的Intent.getData()方法域中返回的。
MediaStore.EXTRA_VIDEO_QUALITY:這個值的范圍是0~1,0的時候質量最差且文件最小,1的時候質量最高且文件最大。
MediaStore.EXTRA_DURATION_LIMIT:這個值以秒為單位,顯示視頻採集的時長。
MediaStore.EXTRA_SIZE_LIMIT:這個值以位元組為單位,限制視頻採集的文件大小。
下面的示例演示了如何構造一個視頻採集的Intent,並執行它。這個例子中的getOutputMediaFileUri()方法引用了下文的「保存媒體文件」中的示例代碼:
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
private Uri fileUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//create new Intent
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO); // create a file to save the video
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // set the video image quality to high
// start the Video Capture Intent
startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
}
當startActivityForResult()方法被執行時,用戶就會看到一個可編輯的Camera應用程序界面。在用戶完成錄像(或取消操作)之後,該用戶界面就會返回到你的應用程序中,並且你必須監聽onActivityResult()方法來接收Intent的結果,並繼續執行你的應用程序。
接收Camera Intent結果
一旦你構建並執行了一個圖片或視頻的Camera Intent,那麼就必須要配置你應用程序來接收Intent的結果。本節向你展示了如何監聽來自Camera Intent的回調,以便應用程序能夠對採集到的圖片或視頻做進一步的處理。
為了接收Intent的結果,必須在啟動Intent的那個Activity中重寫onActivityResult()方法。下面的示例演示了如何重寫onActivityResult()方法來採集圖片Camera Intent或視頻Camera Intent的返回結果:
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// Image captured and saved to fileUri specified in the Intent
Toast.makeText(this, "Image saved to:\n" +
data.getData(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// User cancelled the image capture
} else {
// Image capture failed, advise user
}
}
if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// Video captured and saved to fileUri specified in the Intent
Toast.makeText(this, "Video saved to:\n" +
data.getData(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// User cancelled the video capture
} else {
// Video capture failed, advise user
}
}
}
一旦Activity接收到一個成功的結果,那麼你的應用程序就可以訪問指定位置中的被採集的圖片或視頻。
2. Android 的許可權管理是怎麼實現的
根據用戶的使用過程體驗,可以將 Android 涉及的許可權大致分為如下三類:
(1)Android 手機所有者許可權:自用戶購買 Android 手機後,用戶不需要輸入任何密碼,就具有安裝一般應用軟體、使用應用程序等的許可權;
(2)Android root 許可權:該許可權為 Android 系統的最高許可權,可以對所有系統中文件、數據進行任意操作。出廠時默認沒有該許可權,需要使用 z4Root 等軟體進行獲取,然而,並不鼓勵進行此操作,因為可能由此使用戶失去手機原廠保修的權益。同樣,如果將 Android 手機進行 root 許可權提升,則此後用戶不需要輸入任何密碼,都將能以 Android root 許可權來使用手機。
(3)Android 應用程序許可權:Android 提供了豐富的 SDK(Software development kit),開發人員可以根據其開發 Android 中的應用程序。而應用程序對 Android 系統資源的訪問需要有相應的訪問許可權,這個許可權就稱為 Android 應用程序許可權,它在應用程序設計時設定,在 Android 系統中初次安裝時即生效。值得注意的是:如果應用程序設計的許可權大於 Android 手機所有者許可權,則該應用程序無法運行。如:沒有獲取 Android root 許可權的手機無法運行 Root Explorer,因為運行該應用程序需要 Android root 許可權。
Android 系統許可權定義
Android 系統在 /system/core/private/android_filesystem_config.h 頭文件中對 Android 用戶 / 用戶組作了如下定義,且許可權均基於該用戶 / 用戶組設置。
在 Android 系統中,上述用戶 / 用戶組對文件的訪問遵循 Linux 系統的訪問控制原則,即根據長度為 10 個字元的許可權控制符來決定用戶 / 用戶組對文件的訪問許可權。該控制符的格式遵循下列規則:
第 1 個字元:表示一種特殊的文件類型。其中字元可為 d( 表示該文件是一個目錄 )、b( 表示該文件是一個系統設備,使用塊輸入 / 輸出與外界交互,通常為一個磁碟 )、c( 表示該文件是一個系統設備,使用連續的字元輸入 / 輸出與外界交互,如串口和聲音設備 ),「.」表示該文件是一個普通文件,沒有特殊屬性。
2 ~ 4 個字元:用來確定文件的用戶 (user) 許可權;
5 ~ 7 個字元:用來確定文件的組 (group) 許可權;
8 ~ 10 個字元:用來確定文件的其它用戶 (other user,既不是文件所有者,也不是組成員的用戶 ) 的許可權。
第 2、5、8 個字元是用來控制文件的讀許可權的,該位字元為 r 表示允許用戶、組成員或其它人可從該文件中讀取數據。短線「-」則表示不允許該成員讀取數據。
第 3、6、9 位的字元控制文件的寫許可權,該位若為 w 表示允許寫,若為「-」表示不允許寫。
第 4、7、10 位的字元用來控制文件的製造許可權,該位若為 x 表示允許執行,若為「-」表示不允許執行。
舉個例子,「drwxrwxr--2 rootroot40962 月 11 10:36 lu」表示的訪問控制許可權(黑色字體標明)為:因為 lu 的第 1 個位置的字元是 d,所以由此知道 lu 是一個目錄。第 2 至 4 位置上的屬性是 rwx,表示用戶 root 擁有許可權列表顯示 lu 中所有的文件、創建新文件或者刪除 lu 中現有的文件,或者將 lu 作為當前工作目錄。第 5 至 7 個位置上的許可權是 rwx,表示 root 組的成員擁有和 root 一樣的許可權。第 8 至 10 位上的許可權僅是 r--,表示不是 root 的用戶及不屬於 root 組的成員只有對 lu 目錄列表的許可權。這些用戶不能創建或者刪除 lu 中的文件、執行 junk 中的可執行文件,或者將 junk 作為他們的當前工作目錄。
Android 應用程序許可權申請
每個應用程序的 APK 包裡面都包含有一個 AndroidMainifest.xml 文件,該文件除了羅列應用程序運行時庫、運行依賴關系等之外,還會詳細地羅列出該應用程序所需的系統訪問。程序員在進行應用軟體開發時,需要通過設置該文件的 uses-permission 欄位來顯式地向 Android 系統申請訪問許可權。
3. Android有哪些"許可權"
Android是在Linux內核上建立一個硬體抽象層(Android HAL),通過Dalvik以及各種庫來執行android應用的。在手機啟動時,首先需要由Bootloader(HTC手機上稱作Hboot)引導Linux及手機上各個硬體設備的驅動程序,之後才啟動Android系統。所以其實我們會涉及到四種不同涵義的許可權:
Android許可權(Permission)
這指Android中的一系列"Android.Permission.*"對象,是本文的中心內容。
Google在Android框架內把各種對象(包括設備上的各類數據,感測器,撥打電話,發送信息,控制別的應用程序等)的訪問許可權進行了詳細的劃分,列出了約一百條"Android.Permission"。應用程序在運行前必須向Android系統聲明它將會用到的許可權,否則Android將會拒絕該應用程序訪問通過該"Permission"許可的內容。
比方說,搜狗輸入法提供了一個智能通訊錄的功能,用戶可以在輸入聯系人拼音的前幾個字元,或首字母,輸入法就能自動呈現相關聯系人的名字。為了實現這個功能,輸入法必須聲明它需要讀取手機中聯系人的能力,也就是在相關代碼中加上聲明"android.permission.READ_CONTACTS"對象。
圖5 搜狗輸入法的智能聯系人功能
原生Android只提供了對"一刀切"式的管理,要麼同意使用,否則就根本就不安裝應用程序。當用戶遇到希望使用程序的同時,又想禁止部分Permission的場合,他就無路可走。
於是,不少開發者就搗鼓出了"第三條道路";可惜的是,沒有一種方法能同時做到既不需要將手機固件Root,又完全不涉及對原始應用程序進行反向工程的方法。
RootRoot指獲得Android所在的Linux系統的Root(根)許可權,有了根許可權,你才能對Linux做出任意的修改。iOS中的越獄(Jailbreak) 相當於獲得iOS系統的Root許可權(iOS是一種類Unix系統,和Linux都使用Root的概念)。在已Root的設備中,通常都是使用一個叫"Superuser"(簡稱SU)的應用程序來向許可的程序授以Root許可權。
Bootloader的解鎖(Unlock)
利用數字簽名,Bootloader可以限定只有正確簽名的系統可以被引導。在修改固件以獲得Root以前,解鎖Bootloader通常是必須的。安裝第三方修改、編譯的固件也需要解鎖Bootloader。
基帶(Radio)解鎖
在Android系統中,基帶是上層軟體與手機中無線設備(手機網路,Wi-Fi,藍牙等)的驅動程序之間的中介。國外的網路運營商很喜歡鎖定基帶,從而保證用戶只能使用運營商自己指定的sim卡。在我國,鎖定基帶是非法的,手機製造商、網路運營商也不可以通過鎖定基帶的方法對待違約客戶。iOS的"解鎖"就是解鎖iOS中的基帶軟體。
魚和熊掌不可兼得,Android的世界有很多自由,壞人也能自由地做壞事。它的生態系統很強調自主:用戶可以自主地減小風險,僅使用官方市場的應用程序,也可以自主地解除安全限制,從而獲得更多自由。因此,在遇到壞事的時候,用戶也不得不自主一下:
1, 抵制不道德,乃至非法行為
幾乎所有的Android安全軟體都能對來電、信息進行控制,以減少騷擾。
另一方面,很多應用都會要求它們實際功能以外的許可權,表現在非(主動)告知地搜集設備序列號,位置信息,誘使用戶默認地上傳聯系人列表等方面。
更壞一點的應用程序,則會踏入犯罪的范疇,比如能偷偷發出扣費信息,或是作為黑客的偷窺工具。
2, 減少惡意軟體的損害
惡意軟體即便潛伏成功,也難以獲得許可權,從而減少損失。
3, 用戶有權自主地在抑制應用程序的部分許可權時,繼續使用該應用程序,而只承擔由於自行設置不當而帶來的後果。
用戶擁有設備的所有權,因此有權自主控制設備上的內容、感測器等對象的訪問;同時有權(不)運行,(不)編譯設備上的應用程序。
大多數應用程序在運行時,並未達成主動告知的義務,是失誤;然而即使主動告知,用戶還是可以不理會。
通過Android官方市場,"打包安裝器"安裝應用程序時,所顯示的"許可權"僅是在安裝包內AndroidManifest.xml聲明的值,而非應用程序實際上會調用的內容。該值僅用來表明Android系統能向應用授予的最大可能的許可權。即便一個"Hello World"式的應用程序,也可以在AndroidManifest.xml中聲明所有可能的Android Permission。
這就是說,在AndroidManifest.xml中聲明的值與應用程序實際調用的許可權有關聯,但不等同,且這種提示是由Android系統負責實施的強制行為。
正確的理解是:"應用程序(被迫地)讓Android系統告知用戶,它在AndroidManifest.xml中所聲明的事項。"
這意味著應用程序在使用重要許可權前,依然需要自行、主動地通知用戶相關事宜。
4. 安卓上有沒有無需ROOT就可以禁用應用許可權的東西
1 為什麼Android總是事無巨細地告訴你應用索取的每一項許可權?
相比Apple,Microsoft嚴格控制生態系統(從蘋果給開發者的「App Store Guideline」可見一斑),只允許通過官方應用商店安裝應用,並對每份上傳進行仔細地審查而言,Android的開放就意味著,Google需要向 用戶提供一系列用於為自己負責的流程、工具。所以在安裝應用前,Android總是要事無巨細地告訴你,應用肯需要控制什麼許可權。
同樣,開發者也製作了一系列易用的工具,用以鑒別可疑的應用程序,或是控制許可權。
圖1 Android 官方市場會強制提醒用
Andoird哪裡開放了?
在Android中,用戶能自由從本地安裝應用,自由地對SD卡進行操作,自由選擇應用市場。
如果願意放棄保修,用戶還能輕易地實行root,解鎖基帶(baseband)。只有一些產品會嚴密地鎖定bootloader(如摩托羅拉)。
最重要的是,因為ASOP(Android源代碼開放計劃)的存在,絕大部分的Android代碼都是開源的,開發者可以由此對Android系統進行 深入的修改,甚至可以自行編寫一個符合Android規范的系統實例(如Cyanogen Mod)。正是因為ASOP,這篇文章才可能介紹多達5種原理不同的許可權控制方法。
圖2 Android開源計劃的標志
開放的風險
不考慮Symbian,Windows Phone 6.5(及以下)平台,那麼幾乎所有的智能手機病毒都是Android平台的,甚至官方Android Market也鬧過幾次烏龍。在國內水貨橫行的市場,情況更是火上澆油,不法業者可以在手機的ROM,甚至是bootloader中做好手腳,讓用戶有病無法醫。
在Android中,用戶可以允許系統安裝來自「未知源」(也就是非Google官方的,或手機預置市場的)應用程序。於是,移動平台最重要的門神------數字簽名就被繞過了。
圖3 Android 允許未知安裝未知來源的應用程
出於Android的開放性,也有不允許「未知源」的反例:亞馬遜的Kindle Fire平板使用了深度定製的Android,它只允許安裝來自亞馬遜官方商店的應用程序。
圖4 亞馬遜的 Kindle Fire 僅允許通過自帶的市場安裝應用
2 Android有哪些「許可權」
首先需要明確一下Android中的種種「許可權」。Android是在Linux內核上建立一個硬體抽象層(Android HAL),通過Dalvik以及各種庫來執行android應用的。在手機啟動時,首先需要由Bootloader(HTC手機上稱作Hboot)引導 Linux及手機上各個硬體設備的驅動程序,之後才啟動Android系統。所以其實我們會涉及到四種不同涵義的許可權:
Android許可權(Permission)
這指Android中的一系列「Android.Permission.*」對象,是本文的中心內容。
Google在Android框架內把各種對象(包括設備上的各類數據,感測器,撥打電話,發送信息,控制別的應用程序等)的訪問許可權進行了詳細的劃 分,列出了約一百條「Android.Permission」。應用程序在運行前必須向Android系統聲明它將會用到的許可權,否則Android將會 拒絕該應用程序訪問通過該「Permission」許可的內容。
比方說,搜狗輸入法提供了一個智能通訊錄的功能,用戶可以在輸入聯系人 拼音的前幾個字元,或首字母,輸入法就能自動呈現相關聯系人的名字。為了實現這個功能,輸入法必須聲明它需要讀取手機中聯系人的能力,也就是在相關代碼中 加上聲明「android.permission.READ_CONTACTS」對象。
圖5 搜狗輸入法的智能聯系人功能
原生Android只提供了對「一刀切」式的管理,要麼同意使用,否則就根本就不安裝應用程序。當用戶遇到希望使用程序的同時,又想禁止部分Permission的場合,他就無路可走。
於是,不少開發者就搗鼓出了「第三條道路」;可惜的是,沒有一種方法能同時做到既不需要將手機固件Root,又完全不涉及對原始應用程序進行反向工程的方法。
Root
Root指獲得Android所在的Linux系統的Root(根)許可權,有了根許可權,你才能對Linux做出任意的修改。iOS中的越獄 (Jailbreak) 相當於獲得iOS系統的Root許可權(iOS是一種類Unix系統,和Linux都使用Root的概念)。在已Root的設備中,通常都是使用一個 叫「Superuser」(簡稱SU)的應用程序來向許可的程序授以Root許可權。
Bootloader的解鎖(Unlock)
利用數字簽名,Bootloader可以限定只有正確簽名的系統可以被引導。在修改固件以獲得Root以前,解鎖Bootloader通常是必須的。安裝第三方修改、編譯的固件也需要解鎖Bootloader。
基帶(Radio)解鎖
在Android系統中,基帶是上層軟體與手機中無線設備(手機網路,Wi-Fi,藍牙等)的驅動程序之間的中介。國外的網路運營商很喜歡鎖定基帶,從 而保證用戶只能使用運營商自己指定的sim卡。在我國,鎖定基帶是非法的,手機製造商、網路運營商也不可以通過鎖定基帶的方法對待違約客戶。iOS的「解 鎖」就是解鎖iOS中的基帶軟體。
為什麼要控制Android許可權
魚和熊掌不可兼得,Android的世界有很多自由,壞人也能自由地做壞事。它的生態系統很強調自主:用戶可以自主地減小風險,僅使用官方市場的應用程序,也可以自主地解除安全限制,從而獲得更多自由。因此,在遇到壞事的時候,用戶也不得不自主一下:
1, 抵制不道德,乃至非法行為
幾乎所有的Android安全軟體都能對來電、信息進行控制,以減少騷擾。
另一方面,很多應用都會要求它們實際功能以外的許可權,表現在非(主動)告知地搜集設備序列號,位置信息,誘使用戶默認地上傳聯系人列表等方面。
更壞一點的應用程序,則會踏入犯罪的范疇,比如能偷偷發出扣費信息,或是作為黑客的偷窺工具。
2, 減少惡意軟體的損害
惡意軟體即便潛伏成功,也難以獲得許可權,從而減少損失。
3, 用戶有權自主地在抑制應用程序的部分許可權時,繼續使用該應用程序,而只承擔由於自行設置不當而帶來的後果。
用戶擁有設備的所有權,因此有權自主控制設備上的內容、感測器等對象的訪問;同時有權(不)運行,(不)編譯設備上的應用程序。
大多數應用程序在運行時,並未達成主動告知的義務,是失誤;然而即使主動告知,用戶還是可以不理會。
為什麼Android官方市場的強制提醒許可權的行為不屬於主動告知:
通過Android官方市場,「打包安裝器」安裝應用程序時,所顯示的「許可權」僅是在安裝包內AndroidManifest.xml聲明的值,而非應 用程序實際上會調用的內容。該值僅用來表明Android系統能向應用授予的最大可能的許可權。即便一個「Hello World」式的應用程序,也可以在AndroidManifest.xml中聲明所有可能的Android Permission。
這就是說,在AndroidManifest.xml中聲明的值與應用程序實際調用的許可權有關聯,但不等同,且這種提示是由Android系統負責實施的強制行為。
正確的理解是:「應用程序(被迫地)讓Android系統告知用戶,它在AndroidManifest.xml中所聲明的事項。」
這意味著應用程序在使用重要許可權前,依然需要自行、主動地通知用戶相關事宜。
圖6 應用程序須要AndroidManifest.xml中聲明調用到的許可權
然而,即便只是讓一半的應用程序達到以上的標准,也是不可能的。應用程序需要通過收集用戶信息,程序的錯誤日誌。從而統計用戶的喜好,改進程序。另一方 面,這也是發送精確廣告但不追溯到用戶身份信息的方式,這一點對於免費應用而言,是極其重要的。我們之所以能知道不同型號手機的佔有率,應用軟體的流行 度,是與這樣的統計不可分離的。
一旦每個應用程序都專業地主動發出提醒,不專業的用戶(大多數用戶都是不專業的)通常會將之視為如同海嘯警報一般的危機。
這么做對誰都沒有好處------用戶方的隱私權是毋庸置疑的,然而應用程序方面的獲取信息記錄的需求也是無可阻擋的。如果每個用戶都打算阻止,只會落得被迫接受不平等條約的下場,在溫飽以前,不會有人考慮小康的問題。
於是,現狀就變得有趣:用戶人享受著相同的服務;其中大部分用戶出於不知情/好意,默默地向開發者、廣告商提供了信息,剩下的少數用戶則能阻斷這種勞務。而作為維持Android平台的信息商人Google,只確保在它的地盤里,不會發生觸碰底線的事情。
一句話總結:
設備是我的,不管你怎麼說,反正我說了算,但我說的話大多是不算數的。
3 許可權控制的方法
這里開始介紹各種控制Android許可權的辦法。可惜的是,幾乎所有的手段都需要對設備進行Root,如果不這么做,則需要付出不小代價。
App Shield(國內常見的名字:許可權修改器)
它是一個需要付費的Android應用,其原理是修改應用程序的apk安裝包,刪除其中AndroidManifest.xml文件內,用於聲明許可權的 對應「Android.Permission.*」條目,然後再用一個公開的證書對安裝包重新簽名(需要允許「未知源」),這樣一來,應用程序就不會向系 統申請原先所需的許可權。當應用運行至相應的流程時,系統將直接拒絕,從而達到用戶控制許可權的目的。
對於已安裝的應用,AppShield也會按照同樣方法製作好apk安裝包,然後讓用戶先卸載原始的應用,再安裝調整過的應用。除了該應用數字簽名外,用戶可以隨時通過執行同樣的流程,將吊銷的許可權恢復。
圖7 AppShield
Apk文件的結構
Android應用都是打包成以.apk擴展名結尾,實際上是zip的文件格式。
一個合法的apk至少需要這些成分:
根目錄下的「AndroidManifest.xml」文件,用以向Android系統聲明所需Android許可權等運行應用所需的條件。
根目錄下的classes.dex(dex指Dalvik Exceptionable),應用(application)本身的可執行文件(Dalvik位元組碼) 。
根目錄下的res目錄,包含應用的界面設定。(如果僅是一個後台執行的「service」對象,則不必需)
Apk根目錄下的META-INF目錄也是必須的,它用以存放應用作者的公鑰證書與應用的數字簽名。
當應用被安裝後,這個apk文件會原封不動地移至設備的data/app目錄下,實際運行的,則是Dalvik將其中Classes.dex進行編譯後 的Classes.odex(存放在Dalvik緩存中,刷機時的『cache wipe就是清除Dalvik的odex文件緩存』)。
優點:
完全不需要Root,適用於所有版本的Android設備。不會損壞系統,可以吊銷任意一項Android許可權。
問題:
1,需要重新安裝應用,該行為可能會丟失應用的配置、歷史記錄。
2,執行許可權吊銷的應用的數字簽名會被更改,無法直接更新。對於那些設計不良(沒有意料到『不聲明許可權』情況的),或有額外自校驗的應用,可能會無法運行。
3,無法用於設備上的預裝應用,除非製造商好心地將該應用設置為「可以刪除」的狀態。
4,這個方法修改了apk包中的內容------盡管實際上AndroidManifest.xml和數字簽名並不算是應用程序的本身,但修改它們可能引發著作權的問題。
5,需要開啟「未知源」。
6,這是一個收費應用。
CyanogenMod 7.1(及以上版本)
Cyanogenmod是一款著名的第三方編寫的開源Android ROM。
CM7.1加入了控制許可權的開關,官方的名稱是「Permission Revoking」,任何非系統/保護應用在安裝後,可直接吊銷任意一項許可權,其效果等價於直接刪除apk包中AndroidManifest.xml的 對應條目,但不會引發自校驗的問題。CM的許可權工具的作用等同於AppShield,無非是在Android自身的許可權系統中添加了一個開關。
圖8 Cyanogen Mod 7.1中的許可權吊銷(Permission Revoking)設定
優點:
免費,使用簡便,可隨時,任意地吊銷、恢復非預裝應用的任意一項許可權;不存在數字簽名的問題,因而不影響使用自校驗的應用程序。
問題:
此功能僅在Cyanogen Mod 7.1及以上版本提供,無法用於其它rom。因為是由Android系統出面吊銷許可權,其實現原理與App Shield完全相同,同樣的,應用程序會因為設計不良而出現崩潰。
Permission Denied
這是可以吊銷任意Android應用(注意,不當地吊銷系統應用的許可權可能會導致手機固件損壞,無法啟動)的任意許可權,對許可權的修改在重啟後生效。
實現原理應該與Cyanogen Mod 7.1+完全相同,適用於任何已經Root的系統,因為一般的Android系統雖然事實上支持許可權吊銷,但沒有像Cyanogen Mod那樣放置介面,因此需要重啟後才能應用許可權配置。同樣也有系統出面拒絕許可權而導致的崩潰現象。
圖9 Permission Denied免費版吊銷應用程序許可權的場景
優點:
效果與Cyanogen Mod中的許可權吊銷效果一致,且可吊銷系統應用的許可權。同時提供了免費與收費版本,免費版並沒有基本功能的缺失。適用於所有版本號不低於1.6的Android設備。
問題:
調整後的許可權需要重啟才能生效。設計不良的應用會崩潰。不恰當的許可權修改會損壞系統,導致無法開機。
PDroid
PDroid實際上是一個Android內核補丁加上一個用於管理的外部應用。補丁需要在Recover環境中刷入系統,也可以由開發者自行移植入系 統。該軟體在Android ASOP 2.3.4代碼基礎上開發,僅適用於沒有改動內核的Android 2.3系統,目前還未支持Android 4。
圖10 PDroid的界面
為了避免Cyanogen Mod 7.1+許可權吊銷(Permission revoking)導致的崩潰問題,以及後台服務(如LBE,QQ手機管家等,PDroid的作者認為通過後台服務攔截許可權並不是好辦法),PDroid 並不阻止應用程序聲明許可權,但會在其實際索取相關信息時,予以阻止。通俗地說,就是簽署協議但不執行。在PDroid的用戶界面,用戶能隨時精確地控制涉 及隱私的各項許可權。對於某些內容,除了阻止外,用戶還可以偽造一個隨機或指定的數據。
可控制的內容包括:
IMEI(可偽造)
IMSI(可偽造)
SIM卡序列號(可偽造)
手機號碼(可偽造)
來,去電號碼
SIM卡信息
當前蜂窩網路信息
(以上七者均來自Android.Permission.READ_PHONE_STATE)
GPS定位信息 (可偽造,來自Android.Permission.FINE_LOCATION)
基站定位 (可偽造,來自Android.Permission.COARSE_LOCATION)
系統自帶瀏覽器的歷史,書簽(Android.Permission.BOOKMARKS)
聯系人 (android.permission.READ_CONTACTS)
通話記錄 (android.permission.READ_CONTACTS)
系統日誌 (android.permission.READ_LOGS)
當前賬戶列表 (android.permission.GET_ACCOUNTS)
當前賬戶的授權碼 (android.permission.USE_CREDENTIALS)
簡訊,彩信 (可能與這5個許可權有關)
android.permission.READ_SMS
android.permission.RECEIVE_SMS
android.permission.SEND_SMS
android.permission.WRITE_SMS
android.permission.RECEIVE_MMS
日歷 android.permission.READ_CALENDAR
PDroid的內核補丁並不通用,每一個Rom都需要特定的補丁。開發者除了提供了幾個特定機型下Cyanogen Mod,HTC Sense修改版ROM的專用補丁外,還推出了一個補丁生成工具(PDroid Patcher),用戶可以給自己的ROM生成專用的內核補丁。使用該Patcher需要安裝JDK(java Development Kit)。
優點:
PDroid避免了通過Android系統進行許可權吊銷的導致的潛在崩潰問題,也不需要後台服務。對隱私信息的控制是最精細的。盡管設備必須Root,但應用本身不需要Root許可權。
問題:
安裝過程是最繁瑣,最不可靠的,容易導致ROM損壞,適用范圍也小,需要用戶有相當的技能(能安裝JDK,會刷機)才可使用;只提供對隱私有關許可權的控制,不提供網路訪問,的控制。以這些為代價,它幾乎沒有其它缺點。
LBE安全大師
實際上最常用的是以LBE為代表的通過一個Root許可權的後台服務來攔截相關行為的工具。除了LBE外,還有QQ手機管家等應用。這里以LBE安全大師為例介紹。
圖11 LBE安全大師
LBE是國內一個叫「LBE安全小組」開發的工具,支持Android2.0~4.0。它的核心功能是像殺毒軟體一般,通過一個需要Root許可權的後台 服務,劫持所有調用許可權的行為,並放行用戶許可的部分(其官方宣傳為『API級別攔截』)。它和PDroid一樣幾乎不會引發應用程序崩潰,它支持攔截幾 個涉及用戶的關鍵許可權(LBE手機管家3.1/3.2):
讀取簡訊 (android.permission.READ_CONTACTS)
聯系人記錄 (android.permission.READ_CONTACTS)
通話記錄 (android.permission.READ_CONTACTS)
定位 (Android.Permission.COARSE_LOCATION
Android.Permission.FINE_LOCATION)
手機識別碼 (與Android.Permission.READ_PHONE_STATE有關)
通話狀態 (與Android.Permission.READ_PHONE_STATE有關)
發送簡訊(具體原理不明,同樣類似於禁止這五個許可權
android.permission.READ_SMS
android.permission.RECEIVE_SMS
android.permission.SEND_SMS
android.permission.WRITE_SMS
android.permission.RECEIVE_MMS)
撥打電話 (android.permission.CALL_PHONE)
通話監聽 (android.permission.PROCESS_OUTGOING_CALLS)
除此以外,LBE還可以分別控制應用在Wifi,手機網路的聯網許可權,其原理是依靠IPtables防火牆,而非通過Android的「Internet」許可權。
此外LBE手機管家還提供基於智能內容審查的簡訊攔截、來電歸屬地顯示,以及禁用系統(保護)應用,進程管理,殺毒等功能。
LBE提供兩個版本,一個叫「LBE安全大師」,是一個全面的手機管家類應用,更新比較頻繁,另一個版本(LBE手機隱私衛士,LBE Security lite)僅提供許可權方面的管理。
考慮到主要市場在國內,LBE的發行策略看上去有些奇怪,它在Google的官方市場並不發行最新版。通常只能只能在LBE的官方網頁,以及國內的應用市場獲得最新版本。
優點:
使用非常簡單,功能強大而全面,風險很小,可以控制系統應用。適用范圍廣,有很多替代產品。
問題:
需要後台服務 (盡管蠶豆網有個評測,認為它對能耗幾乎沒有影響),不能控制所有的Android許可權。
4 .自啟動的控制
Android對後台服務有著最好的支持。
在Android中可以自由地開發一種稱為『Service』的後台運行的對象,加上沒有蘋果公司對應用程序的嚴格限制。諸如QQ掛機,即時調用第三方應用程序之類的形式都可以輕易實現。
為了全面支持後台服務,也為了適應移動設備資源緊張,不得不經常清理內存的問題,應用可在系統中設置觸發器,當系統發生了某個特定特定事件時(系統啟動,撥打電話,收發信息,安裝、卸載應用,插上電源等,或應用程序自行定義的事件),就會觸發啟動應用程序。
AutoStarts 自啟動管理
AutoStarts是一個收費應用,通過它,用戶能了解系統中每一項程序會在什麼場合下被觸發運行。如果提供Root許可權,則還能禁止這樣的行為。
這里以Google Maps應用6.2版為例。默認情況下,這款應用總是會保持後台運行,並每小時向Google發送一次當前用戶的位置信息。為了阻止這樣的行為,需要聯合 使用AutoStarts與任意一款進程管理應用:在AutoStarts中,阻止Google Maps的自行啟動(如圖),在每次使用完後,把Google Maps的進程殺掉。
圖12 AutoStarts可以對自啟動項目進行修改
5. 其他
Root帶來的風險
有一個鑽牛角尖的說法認為,一旦對設備進行了Root,便無安全一說,只要惡意程序一旦偷偷獲得Root級別,一切都是空談。
這種說法之所以鑽牛角尖,是因為:一方面Android中的Root許可權通常都是需要用戶通過Superuser應用進行授權的,這已經夠用,雖然不能 指望Superuser無懈可擊;另一方面,控制Android許可權主要是為了讓應用程序在「灰色地帶」的行為收斂一些,它們實際顯然不是病毒等犯罪軟 件。
著作權的問題 (作者不是法律方面的專家,以下言論僅供參考)
我們知 道,Android中的應用程序是基於Java語言編寫的。而為了達到跨平台的目的,Java軟體是以位元組碼(或叫中間代碼,bytecode),而非計 算機能直接執行的機器碼(Machine Code,有時也叫作Binary)的形式存在。因此執行Java軟體時,需要一個Java虛擬機(Android系統中的Java虛擬機就是 Dalvik)負責解釋運行,有的時候,虛擬機還會通過即時編譯(JIT)的方法將位元組碼編譯為機器碼後再運行,以提高程序的執行效率。
這就出現一個很有趣的現象:
除非另行規定,作為設備的擁有者,用戶總是可以自行決定如何使用軟體,能自行決定程序能否訪問用戶自己的計算機(移動設備亦然)裡面的各個內容、對象。
由此衍生出,在需要對代碼編譯、解釋的場合,用戶也能通過對編譯器(解釋器)的干預,來影響代碼的執行效果。在Android中,用戶還可以在Dalvik解釋、編譯的時候動手。
這是因為,著作權僅保護了軟體代碼不受到非授權的反向工程,未授權傳播等侵犯。另一方面,對於Android上的Java,網頁中的 javascript程序,賦予用戶解釋、編譯的權利是程序能執行的先決條件;同時,軟體發行者發通常也會主動提出放棄這種權利(表現為『軟體按原樣提供 』、『不對使用軟體造成的後果負責』等條目)
在編譯、解釋的過程中,需要通過匯編(Assemble),連接(Link)等方法將編譯 好的對象(Object)、方法(Function)聯系起來。默認情況下,這些行為是由原始的代碼(源代碼、中間代碼)與編譯器(解釋器)決定的,但是 用戶可以通過制約編譯器(解釋器)的設置,從而影響到最終代碼。這么做是沒有問題的。
還有一種,應用程序在安裝後,會在系統中產生一些 緩存,或注冊一些信息。當其中的內容有關用戶數據時,讀取或修改它們也是沒有問題的。這就是所謂「只要是你的東西總是你的」;也是Cyanogen Mod、Permission Denied不會涉及版權問題的原因所在。
總之,一個Android應用之所以能運行的前提是:
1,首先,用戶允許使用這個應用
這也可以理解成:用戶安裝了應用(以及因此設定的後台對象),購買了預裝應用的手機。這一點即不影響應用程序的主動通知義務,也不影響用戶事後的干預。
2,接下來,用戶允許Dalvik對該應用使用「解釋」,「JIT」的方法,從而該應用程序得以執行。
3,用戶隨時可以對該應用作出任意不違反版權的干預。
所以,在沒有另行規定的前提下,用戶總是可以自行決定,通過給應用程序分配自定義的許可權;或是在應用程序調取內容,對象時予以阻斷。同時,用戶也需要自行承擔因不當操作產生的後果。
附錄:
1、 數字簽名
數字簽名是一種使用了公鑰加密領域的技術實現,用於鑒別數字信息的方法。一套數字簽名通常定義兩種互補的運算,一個用於簽名,另一個用於驗證。數字簽名可以輕易地驗證完整性(正確性),合法簽署的數字簽名具有不可否認性。 (摘錄自維基網路「數字簽名」條目,有修改)
2、 版權聲明
文章中引用的圖標,圖片或圖片的部分,以及部分文字的引用,僅出於合理使用的目的,可能是持有人版權所有的。
3、 一些行為的說明
不道德行為
應用程序在啟動時,或在主動告知以前,試圖索取、收集電話號碼、郵箱地址、位置信息等與個人身份直接關聯的內容。如果是與個人關聯,但不能直接聯繫到個人信息的IMEI等設備、SIM卡的串號,則稍微好一些。
附圖1,不道德的應用程序在啟動的第一時間就試圖獲取隱私信息
(新浪微博2.8),無論用戶是否綁定了手機,應用都會第一時間記錄當前手機的號碼
(UC瀏覽器,快拍二維碼),應用總是會不主動通知地記錄設備的位置信息
沒有實行主動通知的例子
附圖2 這個應用程序在第一次啟動時便開始收集位置信息,用戶需要切換六次屏幕才能看到有關位置信息的提示。這項提示還有意忽略應用程序本身就會記錄用戶位置信息,即便用戶並不使用需要位置信息的服務
主動通知的例子
附圖3 主動通知就是在第一屏的醒目處,或用醒目的對比色等強調方式進行通告
5. Android 許可權常量保存在哪個類中
1. 保存鍵值對(Saving Key-Value Sets)
如果你要存儲存儲小型的鍵值對(key-value)數據集的話,可以使用SharedPreferences API。SharedPreferences對象是一個包含鍵值對的文件,提供了讀和寫的方法。每一個SharedPreferences文件都被框架(framework)管理,並且既可以是私有的,也可以使公有的。以下內容主要是:使用SharedPreferences APIs 來存儲和檢索簡單的數值。
Write to Shared Preferences
要寫入數據,
1.先在SharedPreferences通過調用edit()方法創建一個SharedPreferences.Editor 。
2.通過putInt()和putString()來把鍵值對寫入,
3.然後調用commit()方法來保存。
For example:
//新建一個SharedPreferences文件,設定為private模式
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
//新建一個editor對象,通過SharedPreferences對象的edit()方法。
SharedPreferences.Editor editor = sharedPref.edit();
//調用editor的putInt()方法,參數就是鍵值對。
editor.putInt(getString(R.string.saved_high_score), newHighScore);
//調用editor的commit()方法,保存。
editor.commit();
Read from Shared Preferences
調用SharedPreferences對象的getInt()方法和getString()方法,讀取SharedPreferences文件。參數可以寫default,也可以寫key。返回值都是value。
//創建一個SharedPreferences對象
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
//參數我default情況
long default = getResources().getInteger(R.string.saved_high_score_default));
//參數為(key,default)情況
long highScore = sharedPref.getInt(getString(R.string.saved_high_score), default);
2.保存文件(saving files)
android系統里對文件進行讀寫,主要使用了File APIs。
File對象主要用來讀寫大的數據量。比如圖像文件、通過網路交換的文件。
以下內容基於讀者已經了解Linux文件系統以及java.io里的標准文件輸入輸出API。
內部存儲Internal storage的特點:
1.一直可用
2.是默認程序的默認文件訪問位置,其他app訪問不了。
3.卸載app時,系統會刪除這個app所有文件
Internal storage 可以確保用戶和其他app無法訪問你的文件。很好的私密性。
外部存儲External storage的特點:
1.並非一直可用。可以插在卡槽,但是卸載掉,這時候系統就看不到了,或者用戶把sd卡當U盤使。
2.文件存儲不安全。sd卡拿出來,裡面的信息就泄露了。
3.卸載app時,只有你在路徑getExternalFilesDir()保存文件的時候,系統才可以刪除這個app的文件。
如果不在這個路徑的話,那SD卡上就會有殘留文件了。
如果文件沒有訪問限制,或者你想讓文件為其他app所用,或者希望通過電腦查看,外部存儲不失為一種好方法。
TIPS:可以設置manifest文件里的android:installLocation屬性來讓app裝在外部存儲。
Obtain Permissions for External Storage
要向外部存儲寫文件,必須在manifest文件里聲明WRITE_EXTERNAL_STORAGE許可權。
<manifest ...>
<uses-permission
//寫入外部存儲文件的許可權
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
...
</manifest>
注意:目前,所有的app都可以讀取外部存儲上的文件,不需要特殊的許可權。但是以後這個肯定要改的,讀取外部存儲的文件也是需要許可權的。所以也要在manifest文件里聲明READ_EXTERNAL_STORAGE許可權。
<manifest ...>
<uses-permission
//讀取外部存儲文件的許可權
android:name="android.permission.READ_EXTERNAL_STORAGE"
/>
...
</manifest>
往內部存儲寫文件是不需要許可權的,從內部存儲讀文件也是不需要許可權的。
所以內外存儲文件還有許可權上的區別:外部的要許可權,內部的不需要。
Save a File on Internal Storage
保存一個文件到內部存儲的時候,需要獲得一個存儲路徑,方法有二:
1.getFilesDir()--------返迴文件File路徑
2.getCacheDir()--------返回一個緩存文件路徑
臨時緩存文件里存放著temporary cache files,確保不用這些文件的時候刪除,並有size上的控制,系統在低存儲空間的時候,會直接刪除這個臨時文件夾內容。
在存儲路徑上創建文件時候,使用File()構造器,參數為路徑,和文件名稱。
//使用File()構造器,參數1:文件路徑 參數2:文件名稱
File file = new File(context.getFilesDir(), filename);
或者,可以調用openFileOutput()方法來獲取FileOutputStream,來向內部存儲路徑寫文件。
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try {
//調用方法創建流,參數1:文件名參數2:文件類型為私有
putStream = openFileOutput(filename, Context.MODE_PRIVATE);
//調用流的write方法
outputStream.write(string.getBytes());
//關閉流
outputStream.close();
}
catch (Exception e) {
e.printStackTrace();
}
如果要創建一個緩存文件,就要用createTempFile()方法。
以下代碼從url提取文件name,然後用那個name在緩存文件夾創建一個文件。
public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
catch (IOException e) {
// Error while creating file
}
return file;
}
注意:app的內部存儲路徑是由你的app的包的名稱指定的,在android文件系統的特定位置。技術上看,如果你把文件mode設置為可讀,其他app是可以讀取你的app的數據的。但是其他app要想讀取,必須要知道你的包的名稱和文件的名稱。除非你明確設定file mode為readable或者writable,否則其他app肯定沒法讀和寫你的internal file。 所以,只要你把文件mode設置為Context.MODE_PRIVATE,其他app是無論如何也沒法訪問到這個文件的。
Save a File on External Storage
因為外部存儲有可能不可用,所以用之前要查詢一下外部存儲的狀態,使用之前肯定要查一下外部存儲的可用空間。調用getExternalStorageState()方法,如果返回的狀態是MEDIA_MOUNTED的話,外部存儲就是可用的,包括讀和寫。
//檢查外部存儲是否可以讀和寫
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
//查看外部存儲是否至少可讀
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
有兩個文件夾來存放文件:
1.公共文件夾(Public files)
其他用戶和app都可以訪問,app卸載以後,文件仍然存在,其他app和用戶可以繼續使用。
比如:app拍的照片
2.私有文件夾(Private files)
文件私有,其他用戶和app無權訪問。app卸載以後,文件即被刪除。盡管文件存儲在外部存儲上,其他用戶
和app是可以訪問的,但是實際上,這些文件是不會向app以外的其他用戶提供數據的。、
比如:app下載的內容和臨時文件
存儲public文件到外部存儲:調用()方法。這個方法有個參數,來指定你要存儲文件的類型,這樣,其他公共文件夾就可以將其包括進去。比如 DIRECTORY_MUSIC和 DIRECTORY_PICTURES。
public File getAlbumStorageDir(String albumName) {
// 獲取用戶公共圖片文件夾路徑
File file = new File(Environment.(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
存儲public文件到外部存儲:調用()方法。這個方法有個參數,來指定你要存儲文件的類型,這樣,其他公共文件夾就可以將其包括進去。比如 DIRECTORY_MUSIC和 DIRECTORY_PICTURES。
public File getAlbumStorageDir(String albumName) {
// 獲取用戶公共圖片文件夾路徑
// 參數1:Environment.(Environment.DIRECTORY_PICTURES)
// 參數2:albumName
File file = new File(Environment.(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
存儲private文件到外部存儲:調用getExternalFilesDir()方法,也要傳入一個指定文件類型的參數。以下是創建一個私人相冊的文件的代碼:
public File getAlbumStorageDir(Context context, String albumName) {
// 獲取應用的私人相冊文件路徑
//參數1:context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
//參數2:albumName
File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
也可以調用 getExternalFilesDir()方法,傳如null參數,這樣就會返回外部存儲的根目錄。
記住:
1. getExternalFilesDir()方法創建的文件路徑,會在用戶卸載這個app的時候被刪除。
2. 如果想讓你的文件路徑在app被卸載以後仍然存在,那麼() 是一個更好的選擇。
3.無論是建立可以共享的文件路徑,還是私有文件路徑,使用API 常量來指定文件目錄類型是很重要的。比如,DIRECTORY_PICTURES,這可以確保系統正確的識別目錄里的數據。
Query Free Space
如果你事先知道要存儲的數據量的大小,你就可以先查詢一下可用空間有多少, 這樣就可以避免IOException,可用的方法是:getFreeSpace()和getTotalSpace()。
getFreeSpace()---返回剩餘空間
getTotalSpace()---返回總空間。
但是返回的可用空間大小,並不說明,你就可以存入這么多的數據。如果空間比你要存入的數據大幾M,或者空間被佔用率小於90%,都還可以,反之,就未必能存入。
並不需要每次存文件之前都來這么個判斷,加個異常捕捉就OK。而且,你也未必知道你要存入的數據有多大,你想判斷,也沒法判斷的,所以能catch這個IOException就行。
Delete a File
刪除文件最簡單的方法是調用文件本身的方法:delete()
myFile.delete();
如果文件存儲在內部存儲介質上,可以利用上下文的方法:deleteFile()
myContext.deleteFile(fileName);
注意:當用戶卸載app時候,系統會刪除以下文件:
1.所有存儲在內部介質上的數據
2.使用getExternalFilesDir()創建路徑的外部介質上的數據
但是,所有緩存文件夾是需要手動刪除的
6. 如何管理android手機中app的許可權
1、(以魅族手機為例)打開手機設置選項。
7. Android許可權機制
我們知道 Android 應用程序是沙箱隔離的,每個應用都有一個只有自己具有讀寫許可權的專用數據目錄。但是如果應用要訪問別人的組件或者一些設備上全局可訪問的資源,這時候許可權機制就能系統化地規范並強制各類應用程序的行為准則。
Android 安全性概覽
在 Android 中,一個許可權,本質上是一個字元串,一個可以表示執行特定操作的能力的字元串。比如說:訪問 SD 卡的能力,訪問通訊錄的能力,啟動或訪問一個第三方應用中的組件的能力。 許可權被授予了之後,首先會在內存和本地中有記錄,這在調用系統binder服務和其他應用組件時做鑒權依據,比如調用系統binder服務時會通過Binder.getCallingUid()拿到調用者的Uid,而Uid一般都是與應用包名一一對應的,再拿這個Uid到PMS里去查這個應用對應的許可權。 其次會按被授予的許可權將應用分到某個組。 可以參考 https://www.jianshu.com/p/a17c8bed79d9
自定義許可權的應用場景在於限制其它應用對本應用四大組件的訪問。具體用法可以參考 https://www.cnblogs.com/aimqqroad-13/p/8927179.html
pm list permissions -f 命令可以詳細查看 Android 所有預定義的許可權。
更詳細的許可權信息參考 https://developer.android.com/reference/android/Manifest.permission?hl=zh-cn#WRITE_EXTERNAL_STORAGE
可以看到一個許可權的信息包括:定義的包名、標簽、描述、 許可權組 和 保護級別 。
許可權根據設備的功能或特性分為多個組。如果應用已在相同許可權組中被授予另一危險許可權,系統將立即授予該許可權,如READ_CONTACTS和WRITE_CONTACTS。
SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 由於其特殊性,其申請方式與其它許可權都不同。
其授予流程如下:
(關於 AppOpsManager 是什麼可以參考: https://segmentfault.com/a/1190000009214983 )
這里簡要分析下ActivityCompat#requestPermissions的流程:
更詳細的許可權授予流程源碼分析可以參考: https://segmentfault.com/a/1190000009214983
普通許可權: 清單文件中聲明即可。
危險許可權: 方式一: pm grant application_package android.permission.CHANGE_CONFIGURATION 方式二:appops set application_package permission_num 0/1
appops可以授予的許可權參考 android.app.AppOpsManager 中的聲明
系統簽名許可權: 方式一:將app遷移到system/priv-app目錄中。 方式二:看不懂,參考 https://blog.csdn.net/abcd_3344_abcd/article/details/50698759
android 4.4 訪問sd卡需要申請許可權。 您的應用在 Android 4.4 上運行時無法讀取外部存儲空間上的共享文件,除非您的應用具有 READ_EXTERNAL_STORAGE 許可權。也就是說,沒有此許可權,您無法再訪問 () 返回的目錄中的文件。但是,如果您僅需要訪問 getExternalFilesDir() 提供的您的應用特有目錄,那麼,您不需要 READ_EXTERNAL_STORAGE `許可權。
android 6.0 運行時許可權。 此版本引入了一種新的許可權模式,如今,用戶可直接在運行時管理應用許可權。這種模式讓用戶能夠更好地了解和控制許可權,同時為應用開發者精簡了安裝和自動更新過程。用戶可為所安裝的各個應用分別授予或撤銷許可權。 對於以 Android 6.0(API 級別 23)或更高版本為目標平台的應用,請務必在運行時檢查和請求許可權。要確定您的應用是否已被授予許可權,請調用新增的 checkSelfPermission() 方法。要請求許可權,請調用新增的 requestPermissions() 方法。即使您的應用並不以 Android 6.0(API 級別 23)為目標平台,您也應該在新許可權模式下測試您的應用。 如需了解有關在您的應用中支持新許可權模式的詳情,請參閱 使用系統許可權 。如需了解有關如何評估新模式對應用的影響的提示,請參閱 許可權最佳做法 。
android 7.+ 應用間共享文件要使用FileProvider。 對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file://URI。如果一項包含文件 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常。 要在應用間共享文件,您應發送一項 content:// URI,並授予 URI 臨時訪問許可權。進行此授權的最簡單方式是使用 FileProvider `類。如需了解有關許可權和共享文件的詳細信息,請參閱 共享文件 。
android 8.+
同一許可權組的許可權在被授予了之後也需要顯式的再申請一次。
在 Android 8.0 之前,如果應用在運行時請求許可權並且被授予該許可權,系統會錯誤地將屬於同一許可權組並且在清單中注冊的其他許可權也一起授予應用。 對於針對 Android 8.0 的應用,此行為已被糾正。系統只會授予應用明確請求的許可權。然而,一旦用戶為應用授予某個許可權,則所有後續對該許可權組中許可權的請求都將被自動批准。 例如,假設某個應用在其清單中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 。應用請求 READ_EXTERNAL_STORAGE ,並且用戶授予了該許可權。如果該應用針對的是 API 級別 24 或更低級別,系統還會同時授予 WRITE_EXTERNAL_STORAGE ,因為該許可權也屬於同一 STORAGE 許可權組並且也在清單中注冊過。如果該應用針對的是 Android 8.0,則系統此時僅會授予 READ_EXTERNAL_STORAGE ;不過,如果該應用後來又請求 WRITE_EXTERNAL_STORAGE ,則系統會立即授予該許可權,而不會提示用戶。
android 9
隱私許可權變更。
為了增強用戶隱私,Android 9 引入了若干行為變更,如限制後台應用訪問設備感測器、限制通過 Wi-Fi 掃描檢索到的信息,以及與通話、手機狀態和 Wi-Fi 掃描相關的新許可權規則和許可權組。
android 10
隱私權變更。
外部存儲訪問許可權范圍限定為應用文件和媒體,在後台運行時訪問設備位置信息需要許可權,針對從後台啟動 Activity 的限制等。
android 11
隱私許可權變更。
更詳細的版本變更請參考 https://developer.android.com/preview/privacy?hl=zh-cn