Android系統權限那些事

Android6.0帶來了新的權限管理方式,本文主要來源於官方文檔,加入了本身的理解,目的是想總結Android6.0權限管理的新方式,其餘部分可能主要是總結式的帶過,後續再詳細分析。html


一.Security Architecture(安全體系結構)


Android安全體系結構的核心是:java

默認狀況下沒有任何應用有權限去執行對其餘應用、操做系統、用戶有不利影響的操做。這是一個核心的設計理念。記住這句話對後面的權限管理能夠很好的理解。android


正式因爲這樣的設計理念,默認狀況下應用不能去讀寫用戶的私有數據(好比Email和Contacts),不能去讀寫其餘App的文件,不能執行網絡訪問,不能保持設備始終喚醒等等。api


由於每個Android 應用都是在一個進程沙盒中運行的,應用必須明確分享的資源和數據,經過申明須要的額外權限這種方式(這些額外權限不禁基本沙河提供)。應用靜態的聲明這些權限(在Manifest裏面),而後Android系統會請求用戶贊成這些權限。
安全


Android應用沙盒不依賴於建立應用的技術(這句話不是很懂,懂的大神能夠指一下),特別的是Dalvik虛擬機並非安全邊界的,全部的應用均可以運行native code(好比參見NDK),全部類型的應用-Java,native,hybird,都是以一樣的方式封裝在沙盒內而且相互之間是一樣的安全等級。網絡


二.Application signing(應用簽名)


全部的Apk文件都必須由他的開發者使用私有的簽名證書籤名,這個證書是開發者身份的惟一標識,這個證書是由開發者本身生成的開放式的證書,用於本身簽名應用。證書的目的是標識應用的身份,這樣可讓系統知道是該容許仍是拒絕應用訪問簽名級別的權限(signature-level permissions),以及容許仍是拒絕應用所請求的給予相同Linux身份來做爲不一樣的應用。app


三.User IDs and File Access(用戶ID和文件訪問)


在安裝一個app package的時候,android系統會給每個package一個獨立的Linux user ID。這個User ID在這個應用在當前設備的生命週期內都是固定不變的,在不一樣的設備,相同的package的用戶ID可能各不相同,但能夠肯定的是在一臺設備上一個package的用戶ID是固定不變的。async


由於安全執行是發生在進程層面的,兩個不一樣的package不能運行在相同的進程中,他們會被做爲不一樣的Linux用戶來運行。ide

可是你能夠在manifest中使用sharedUserId屬性來指定不一樣的package有相同的User ID,這樣這兩個不一樣的package將會被視爲相同的APP,會有相同的User ID和文件權限。ui

固然爲了保證安全,只有兩個APP簽名一致且申明瞭相同的sharedUserId纔會被給予相同的User ID。


四.Using Permission(使用權限)


一個新建的Android應用默認是沒有權限的,這意味着它不能執行任何可能對用戶體驗有不利影響的操做或者訪問設備數據。爲了使用受保護的功能,你必須包含一個或者多個<uses-permission>標籤在你的app manifest中。


Android 6.0中權限分爲兩種,普通權限和危險權限(即運行時權限,下面統稱運行時權限)。


4.1普通權限

若是你的應用manifest中只申明瞭普通權限(也就是說,這些權限對於用戶隱私和設備操做不會形成太多危險),系統會自動授予這些權限。

4.2運行時權限

若是你的應用manifest中聲明瞭運行時權限(也就是說,這些權限可能會影響用戶隱私和設備的普通操做),系統會明確的讓用戶決定是否授予這些權限。系統請求用戶授予這些權限的方式是由當前應用運行的系統版原本決定的。

4.2.1 Android6.0及以上的系統

若是你的設備運行的是Android6.0(API level 23)及以上的系統,而且你的應用的targetSdkVersion也是23或者更高,那麼應用向用戶請求這些權限是實時的。這意味着用戶能夠隨時取消這些運行時權限的受權。因此應用在每次須要用到這些運行時權限的時候都須要去檢查是否還有這些權限的受權。具體請求方式後面會講到。

4.2.2 Android 5.1及如下的系統

若是你的設備運行在Android5.1(API level 22)及如下的系統中,或者你的app的targetSdkVersion是22或者更低。系統會請求用戶在apk安裝的時候授予這些權限。

若是你的應用更新的時候添加了一個權限,系統會在用戶更新應用的時候請求用戶授予這個權限,一旦用戶安裝了這個應用,惟一能夠取消受權的方式就是卸載掉這個應用。注意這句話的意思,想一下若是app的targetSdkVersion是22或者如下,可是運行在Android6.0及以上的設備中會有什麼問題?後面會分析這個問題。


經常來講一個受權失敗會拋出SecurityException,然而這並非在全部狀況下都會發生。好比,發送一個廣播去檢查受權(SendBroadcast(Intent)),數據會被髮送給全部接收者,可是當這個方法的請求返回的時候,你不會收到任何一個由於受權失敗拋出的異常,其實在大多數狀況下,受權失敗只會打印系統日誌。


任何應用也能夠定義和執行他們本身的權限


4.3自動權限調整

簡單的說,若是你的app targetSdkVersion是3,而你當前運行的系統版本是4,那麼在android version 4 中新添加的權限會自動添加到你的app中。

好比WRITE_EXTERNAL_STORAGE權限是在api 4的時候添加的,而你的應用的targetSdkVersion是3,那麼這個權限會自動添加到你的應用中。並且在官方商店上這個權限也會列出來(儘管可能你並不須要這個權限)。

因此建議常常更新你的targetSdkVersion到最新版本。


下面來回答上面的那個問題,若是app的targetSdkVersion是22或者如下,可是運行在android 6.0或以上版本的手機中,會發生什麼?


安裝過程當中,會一塊兒請求用戶授予全部權限,若是用戶拒絕,將不能安裝這個app,只有用戶所有贊成這些受權,才能安裝這個應用,可是問題來了,安裝好了這個應用以後,android6.0以上的系統中,用戶是能夠去設置中取消受權的,並且是隨時均可以取消,因此不少運行時權限可能也得不到,目前官方的作法是,若是用戶取消該項受權,那麼依賴該項受權的方法的返回值爲null,因此你的app可能會報空指針異常。之後是否會針對22如下的app作改變還不得而知,畢竟crash是很難讓人接受的,可是crash是由用戶形成的,用戶應該也能夠理解。


五.普通權限和運行時權限


系統權限會被傳遞給兩種不一樣的保護級別,咱們所知道這兩種最重要的保護級別就是普通權限和運行時權限。


5.1普通權限

普通權限的覆蓋區域是在你的app須要訪問沙盒之外的數據和資源的時候,可是對用戶隱私和其餘app的操做只有不多的影響,好比開啓手電筒的權限。這個時候,系統會自動受權這些普通權限。


5.2運行時權限

運行時權限的覆蓋區域是你的app想要的數據和資源涉及用戶的隱私信息,或者是可能潛在的影響用戶的存儲數據或者其餘app的操做。好比,請求獲取用戶聯繫人信息的權限。若是一個app申明瞭運行時權限,用戶必須明確的受權這些權限給app。


5.3權限組

全部的運行時權限都屬於對應的權限組,若是你的app運行在android6.0及以上系統,下面的規則都適用:

5.3.1

若是一個app在manifest中請求了一個運行時權限,並且app尚未獲得這個運行時權限所在的權限組中的任何一個運行時權限受權,那麼系統會彈出一個對話框,描述app想要訪問的運行時權限的權限組,這個對話框不會描述這個權限組中某一個特定的權限。

好比,你的app想要請求READ_CONTACT權限,對話框只會描述app想要請求設備的聯繫人,若是用戶受權經過,系統會授予app所請求的該項權限。

5.3.2

若是一個app在manifest中請求一個運行時權限,而且這個app已經在相同的權限組中有了另外一個運行時權限的受權,那麼系統不會彈出對話框,而是會當即授予app該項運行時權限的受權。

好比,一個app以前請求過一個READ_CONTACT的權限而且被受權經過,以後又請求一個WRITE_CONTACT(在同一個權限組)權限,那麼系統會自動授予該權限。


其實全部權限(普通權限、運行時權限、用戶自定義權限)都屬於特定的權限組,可是隻有運行時權限的權限組纔會影響用戶體驗。


5.3.3運行時權限和權限組列表
Permission Group Permissions
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE    


有兩個權限比較特殊,分別是SYSTEM_ALERT_WINDOWWRITE_SETTINGS

都屬於比較敏感的權限,不少時候都永不到,若是須要這個權限,應該首先聲明,而後發送Intent去請求用戶受權,而後系統會顯示詳細的管理界面。


5.3.4運行時權限的申請

第一.判斷

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M

第二.若是是android6.0以上的系統,則檢查是否獲取受權

int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
       
Manifest.permission.WRITE_CALENDAR);

若是返回值爲PackageManager.PERMISSION_GRANTED,則能夠繼續以後的操做,若是返回值爲PERMISSION_DENIED,則表明沒有受權該權限。

第三.shouldShowRequestPermissionRationale()能夠獲得是否須要彈出一個解釋申請該權限的提示給用戶,若是爲true,則能夠彈。

第四.請求該權限

示例以下:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
               
Manifest.permission.READ_CONTACTS)
       
!= PackageManager.PERMISSION_GRANTED) {

   
// Should we show an explanation?
   
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
           
Manifest.permission.READ_CONTACTS)) {

       
// Show an expanation to the user *asynchronously* -- don't block
       
// this thread waiting for the user's response! After the user
       
// sees the explanation, try again to request the permission.

   
} else {

       
// No explanation needed, we can request the permission.

       
ActivityCompat.requestPermissions(thisActivity,
               
new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS
);

       
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
       
// app-defined int constant. The callback method gets the
       
// result of the request.
   
}
} else {
   執行獲取權限後的操做
}


第五.請求權限以後,在onRequestPermissionsResult()返回值中能夠獲得用戶是否受權,若是受權,就能夠操做該運行時權限對應的方法

@Override
public void onRequestPermissionsResult(int requestCode,
       
String permissions[], int[] grantResults) {
   
switch (requestCode) {
       
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
           
// If request is cancelled, the result arrays are empty.
           
if (grantResults.length > 0
               
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {

               
// permission was granted, yay! Do the
               
// contacts-related task you need to do.

           
} else {

               
// permission denied, boo! Disable the
               
// functionality that depends on this permission.
           
}
           
return;
       
}

       
// other 'case' lines to check for other
       
// permissions this app might request
   
}
}


運行時權限的特色是,實時性,用戶能夠隨時取消受權,因此每次調用運行時權限的方法都須要判斷或者請求一次運行時權限。


在執行運行時權限申請的同時想一下是否真的有必要,想一下使用Intent的方式啓動其餘應用是否能夠達到需求,好比ACTION_IMAGE_CAPTURE,是直接申明CAMERA的權限本身作一個照相機仍是發送ACTION_IMAGE_CAPTURE請求讓別的應用處理並在onActivityResult()返回值更方便



若是設備運行在5.1或者如下的設備,或者targetSdkVersion在22或如下,系統會在安裝app的時候讓用戶受權權限。再說一遍,系統只會提示用戶app須要的權限組,而不會提示某一個特定的權限。


六.自定義權限


爲了執行自定義權限,你必須在你的manifest中聲明一個或多個<permission>標籤。

好比:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.me.app.myapp" >
    <permission android:name="com.me.app.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity"
        android:permissionGroup="android.permission-group.COST_MONEY"
        android:protectionLevel="dangerous" />
    ...
</manifest>

<protectionLevel>屬性是必須的,告訴系統當app申請該權限的時候,要怎樣通知用戶。

<permissionGroup>屬性是可選的,能夠幫助系統顯示自定義屬性屬於哪一個權限組,當通知用戶彈出框的時候,固然你能夠選擇某一個自定義權限屬於已知的權限組,也能夠屬於某一個自定義權限組,建議屬於已知的權限組。

<android:label>至關於權限組的提示,要簡短

<android:description>是某一個特定權限的描述,規則是兩句話,第一句描述,第二句警告用戶若是受權會發生什麼後果。


好比,CALL_PHONE權限

<string name="permlab_callPhone">directly call phone numbers</string>
    <string name="permdesc_callPhone">Allows the application to call
        phone numbers without your intervention. Malicious applications may
        cause unexpected calls on your phone bill. Note that this does not
        allow the application to call emergency numbers.</string>

以後,你會在setting—>Application中看到該自定義權限。

相關文章
相關標籤/搜索