Android M 版本以后的特殊權限問題分析


 

現象

桌面懸浮框在6.0以后,會因為SYSTEM_ALERT_WINDOW權限的問題,無法在最上層顯示。

問題原因

  • SYSTEM_ALERT_WINDOW,設置懸浮窗,進行一些黑科技
  • WRITE_SETTINGS 修改系統設置

SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS, 這兩個權限比較特殊,不能通過代碼申請方式獲取,必須得用戶打開軟件設置頁手動打開,才能授權。
路徑是:Settings -> Apps -> xxx -> Draw over other apps . 然后手動打開應用的此權限。

只靠Manifest申請該權限是無效的。

 

 

  注意:
  • 預置應用應該是可以默認使用該權限的(經驗說:預置應用默認開啟所需要的權限,就算在apps->permission中顯示的權限默認是關閉的)。
  • 通過Google Play Store(Version 6.05 or heigher is required)下載的需要該權限的應用,會被自動授予該權限

參考如下:

It is a new behaviour introduced in Marshmallow 6.0.1.
Every app that requests the SYSTEM_ALERT_WINDOW permission and that is installed through the Play Store (version 6.0.5 or higher is required), will have granted the permission automatically.
If instead the app is sideloaded, the permission is not automatically granted. You can try to download and install the Evernote APK from apkmirror.com. As you can see you need to manually grant the permission in Settings -> Apps -> Draw over other apps.
These are the commits [1] [2] that allow the Play Store to give the automatic grant of the SYSTEM_ALERT_WINDOW permission.
From: SYSTEM_ALERT_WINDOW - How to get this permission automatically on Android 6.0 and targetSdkVersion 23

以及源碼中對該部分的修改說明。注:在MTK 平台上目前未合入此條記錄。

 

規避方法

對於SYSTEM_ALERT_WINDOW,官方建議需要申請該權限時引導用戶跳轉到Setting中自己去開啟權限開關

//參考自http://stackoverflow.com/questions/32061934/permission-from-manifest-doesnt-work-in-android-6
 1 public static int OVERLAY_PERMISSION_REQ_CODE = 1234;
 2 
 3 @TargetApi(Build.VERSION_CODES.M)
 4 public void requestAlertWindowPermission() {
 5     if (!Settings.canDrawOverlays(MainActivity.this)) {
 6         Toast.makeText(this, "can not DrawOverlays", Toast.LENGTH_SHORT).show();
 7         Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + MainActivity.this.getPackageName()));
 8         startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
 9     } else {
10       // Already hold the SYSTEM_ALERT_WINDOW permission, do addview or something.
11     }
12 }
13 
14 @TargetApi(Build.VERSION_CODES.M)
15 @Override
16 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
17     if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
18         if (!Settings.canDrawOverlays(this)) {
19             // SYSTEM_ALERT_WINDOW permission not granted...
20             Toast.makeText(this, "Permission Denieddd by user.Please Check it in Settings", Toast.LENGTH_SHORT).show();
21         } else {
22             Toast.makeText(this, "Permission Allowed", Toast.LENGTH_SHORT).show();
23             // Already hold the SYSTEM_ALERT_WINDOW permission, do addview or something.
24         }
25     }
26 }

上述代碼需要注意的是

  • 使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION啟動隱式Intent
  • 使用"package:" + getPackageName()攜帶App的包名信息
  • 使用Settings.canDrawOverlays方法判斷授權結果

 

對於WRITE_SETTINGS,官方建議相同的方案:

 1 private static final int REQUEST_CODE_WRITE_SETTINGS = 2;
 2 private void requestWriteSettings() {
 3     Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
 4     intent.setData(Uri.parse("package:" + getPackageName()));
 5     startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
 6 }
 7 @Override
 8 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 9     super.onActivityResult(requestCode, resultCode, data);
10     if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
11         if (Settings.System.canWrite(this)) {
12             Log.i(LOGTAG, "onActivityResult write settings granted" );
13         }
14     }
15 }

上述代碼需要注意的是

  • 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 啟動隱式Intent
  • 使用"package:" + getPackageName()攜帶App的包名信息
  • 使用Settings.System.canWrite方法檢測授權結果

注意:關於這兩個特殊權限,一般不建議應用申請。

 

 

安卓7.0項目問題

首先分析以下log:

10-13 00:36:21.425979 5368 5368 E AndroidRuntime: FATAL EXCEPTION: main 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: Process: com.android.settings, PID: 5368
10-13 00:36:21.425979 5368 5368 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity 
{com.android.settings/com.android.settings.DeviceAdminAdd}: java.lang.SecurityException:
com.google.android.gms from uid 10012 not allowed to perform SYSTEM_ALERT_WINDOW 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3506) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3546) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2795) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.-wrap12(ActivityThread.java) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:110) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.os.Looper.loop(Looper.java:203) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6251) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: Caused by: java.lang.SecurityException:
com.google.android.gms from uid 10012 not allowed to perform SYSTEM_ALERT_WINDOW 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.AppOpsManager.checkOp(AppOpsManager.java:1719) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at com.android.settings.DeviceAdminAdd.onResume(DeviceAdminAdd.java:454) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.Activity.performResume(Activity.java:6770) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3477) 10-13 00:36:21.425979 5368 5368 E AndroidRuntime: ... 10 more

 

com.google.android.gms 是google的 “Play商店” 的包名,和 “Google Play Music” 應用一起預置在手機中。

在 “com.android.settings.applications.InstalledAppDetails#hasPermission” 中打log,獲取到每個應用申請的所有權限;

“Google Play Music”有以下幾個權限:

25562:01-01 16:56:35.079 19048 19048 D Neo     : per == android.permission.ACCESS_FINE_LOCATION
25563:01-01 16:56:35.079 19048 19048 D Neo     : per == android.permission.WRITE_SETTINGS
25564:01-01 16:56:35.079 19048 19048 D Neo     : per == android.permission.SYSTEM_ALERT_WINDOW
25565:01-01 16:56:35.079 19048 19048 D Neo     : per == android.permission.ACCESS_FINE_LOCATION
25566:01-01 16:56:35.079 19048 19048 D Neo     : per == android.permission.WRITE_SETTINGS

申請了 android.permission.WRITE_SETTINGSandroid.permission.SYSTEM_ALERT_WINDOW 兩個權限,所以在 Settings --> App --> 相應的app中,就能找到對應的 “在其他應用上層顯示” 和 “修改系統設置” 的相關配置。

 

“Play商店” 沒有申請 android.permission.WRITE_SETTINGS 和 android.permission.SYSTEM_ALERT_WINDOW 兩個權限,所以Settings中也不會給這個應用修改權限的機會。

因為在 “com.android.settings.applications.InstalledAppDetails#addDynamicPrefs” 中,會首先判斷應用是否在AndroidManifest.xml中聲明了特殊權限,如果有,則顯示出“在其他應用上層顯示” 和 “修改系統設置” 的相關配置控件,否則就隱藏。

 

因此,雖然不建議主動應用申請,但是如果報出這樣的問題,應該首先在應用內部申請權限,然后如上所示,引導用戶去Settings中手動開啟權限。

或者,在 “com.android.server.pm.DefaultPermissionGrantPolicy” 中給應用添加默認權限,這個類只有在首次開機的時候才會被調用。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM