Android 6 授權架構
應用程式若需要使用到系統聯絡人等個人機密資料時, 需於設定檔加入授權設定. 此時使用者下載安裝時, 會彈出是否允許授權. 這種機制可以防止惡意程式在暗中搞鬼偷取個人資料.
設定檔授權設定如下, 需寫於<application>標簽的上面
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.ddns.mahaljsp.ch11_02_system_action" > <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <application> .... </application>
但這種授權模式, 有個缺失, 就是如果應用程式要求10幾個授權, 使用者要一一允許授權. 若其中一項不允許, 應用程式就不會安裝
到了Android 6.0(API 23)開始, 使用新的授權架構, 將授權分二大類
Normal permissions : 一般授權, 不影響使用者隱私的授權, 安裝時系統會自動授權
Dangerous permissions : 危險授權, 可以存取使用者隱私資料的授權. 使用者運作此應用程式時, 需執行授權工作, 開發人員需執行相關的設計與判斷
也就是說, 在Android 6.0中, 下載安裝應用程式時, 並不會要求允許授權, 而是在執行中若需要使用到危險授權, 才會彈出詢問.
這兩種權限同樣都要在AndroidManifest.xml中使用<uses-permission>宣告. 在執行應用程式時, 若要存取危險權限, 還需要使用requestPermissions()方法, 此時畫面會出現請求權限的對話框, 要求使用者允許存取資料.
危險權限依照功能分為以下幾個組別:
No | Group | Item | Remark |
1 | CALENDAR日曆 | READ_CALENDAR | 讀取日曆 |
WRITE_CALENDAR | 寫入日曆 | ||
2 | CAMERA | CAMERA | 相機拍照功能 |
3 | CONTACTS聯絡人 | READ_CONTACTS | 讀取聯絡人 |
WRITE_CONTACTS | 寫入聯絡人 | ||
GET_ACCOUNTS | 取得手機帳號 | ||
4 | LOCATION位置 | ACCESS_FINE_LOCATION | 取得精確位置 |
ACCESS_COARSE_LOCATION | 取得大約位置 | ||
5 | MICROPHONE麥克風 | RECORD_AUDIO | 錄製聲音 |
6 | PHONE電話 | READ_PHONE_STATE | 讀取通話狀態 |
CALL_PHONE | 撥出電話 | ||
READ_CALL_LOG | 讀取通話記錄 | ||
WRITE_CALL_LOG | 寫入通話記錄 | ||
ADD_VOICEMAIL | 新增語音留言 | ||
USE_SIP | 使用SIP網路電話 | ||
PROCESS_OUTGOING_CALLS | 存取撥出電話 | ||
7 | SENSORS感應器 | BODY_SENSORS | 讀取體感資料 |
8 | SMS簡訊 | SEND_SMS | 傳送簡訊 |
RECEIVE_SMS | 接收簡訊 | ||
READ_SMS | 讀取簡訊 | ||
RECEIVE_WAP_PUSH | 接收WAP推播訊息 | ||
RECEIVE_MMS | 接收多媒體簡訊 | ||
9 | STORAGE儲存 | READ_EXTERNAL_STORAGE | 讀取外部儲存 |
WRITE_EXTERNAL_STORAGE | 寫入外部儲存 |
public class MainActivity extends AppCompatActivity { private static final int REQUEST_LOCATION_PERMISSION=100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); processPermission(); } private void processPermission(){ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){ int hasPermission=checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION); if(hasPermission!= PackageManager.PERMISSION_GRANTED){ requestPermissions( new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION); } else{ init(); } } else{ init(); } } @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode==REQUEST_LOCATION_PERMISSION){ if (grantResults[0]==PackageManager.PERMISSION_GRANTED){ init(); } else{ Toast.makeText(this, "No permission", Toast.LENGTH_LONG).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void init(){ Intent intent=new Intent(); intent.setAction(Intent.ACTION_CALL); Uri uri=Uri.parse("tel:0987000000"); intent.setData(uri); startActivity(intent); } }
上述程式碼中, 利用 Build.VERSION.SDK_INT>=Build.VERSION_CODES.M 來判斷目前的裝置版本是否大於23. 如果是大於23, 就要用Activity的checkSelfPermission(String)方法來檢查是否有授權. 如果檢查結果不是 PackageManager.PERMISSION_GRANTED, 就要用Activity的 requestPermissions() 方法請求授權. 當結束授權對話方框後, 會回到Activity的
public void onRequestPermissionsResult() 方法, 所以在這方法中再檢查一次是否得到了PackageManager.PERMISSION_GRANTED, 如果得到授權了, 就開始撥打電話. 如果沒得到授權, 就顯示權限不足
多項權限
AndroidManifest.xml 加入如下三項權限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
public class MainActivity extends AppCompatActivity { String perms[]=new String[3]; final public static int REQUEST_PERMISSION=100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); perms[0] = Manifest.permission.ACCESS_FINE_LOCATION; perms[1] = Manifest.permission.CAMERA; perms[2] = Manifest.permission.WRITE_EXTERNAL_STORAGE; processPermission(); } private void processPermission(){ if(Build.VERSION.SDK_INT<Build.VERSION_CODES.M) { init(); } else{ if(checkSelfPermission(perms[0])!= PackageManager.PERMISSION_GRANTED || checkSelfPermission(perms[1])!= PackageManager.PERMISSION_GRANTED || checkSelfPermission(perms[2])!= PackageManager.PERMISSION_GRANTED){ requestPermissions(perms, REQUEST_PERMISSION); } else{ init(); } } } private void init(){}; public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode!=REQUEST_PERMISSION){ super.onRequestPermissionsResult(requestCode, permissions, grantResults); } else{ Map<String, Integer> map = new HashMap<String, Integer>(); for (int i = 0; i < permissions.length; i++) { map.put(permissions[i], grantResults[i]); } if (map.get(perms[0]) == PackageManager.PERMISSION_GRANTED && map.get(perms[1]) == PackageManager.PERMISSION_GRANTED && map.get(perms[2]) == PackageManager.PERMISSION_GRANTED) { init(); } else{ Toast.makeText(this, "No permission", Toast.LENGTH_SHORT).show(); finish(); } } } }