从Settings.canDrawOverlays到Binder.getCallingPid
在调试程序的时候,发现一个奇怪的问题,在主线程调用Settings.canDrawOverlays判断是否有悬浮权限的结果和在另外一个service线程中调用Settings.canDrawOverlays的结果不一样,奇怪。
事出反常必有妖,我们去看看谁在作怪。
查看其定义
frameworks/base/core/java/android/provider/Settings.java
2225 public static boolean canDrawOverlays(Context context) {
2226 return Settings.isCallingPackageAllowedToDrawOverlays(context, Process.myUid(),
2227 context.getOpPackageName(), false);
2228 }
再调用
13247 public static boolean isCallingPackageAllowedToDrawOverlays(Context context, int uid,
13248 String callingPackage, boolean throwException) {
13249 return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
13250 callingPackage, throwException, AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
13251 PM_SYSTEM_ALERT_WINDOW, false);
13252 }
13277 public static boolean isCallingPackageAllowedToPerformAppOpsProtectedOperation(Context context,
13278 int uid, String callingPackage, boolean throwException, int appOpsOpCode, String[]
13279 permissions, boolean makeNote) {
13280 if (callingPackage == null) {
13281 return false;
13282 }
13283
13284 AppOpsManager appOpsMgr = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
13285 int mode = AppOpsManager.MODE_DEFAULT;
13286 if (makeNote) {
13287 mode = appOpsMgr.noteOpNoThrow(appOpsOpCode, uid, callingPackage);
13288 } else {
13289 mode = appOpsMgr.checkOpNoThrow(appOpsOpCode, uid, callingPackage);
13290 }
13291
13292 switch (mode) {
13293 case AppOpsManager.MODE_ALLOWED:
13294 return true;
13295
13296 case AppOpsManager.MODE_DEFAULT:
13297 // this is the default operating mode after an app's installation
13298 // In this case we will check all associated static permission to see
13299 // if it is granted during install time.
13300 for (String permission : permissions) {
13301 if (context.checkCallingOrSelfPermission(permission) == PackageManager
13302 .PERMISSION_GRANTED) {
13303 // if either of the permissions are granted, we will allow it
13304 return true;
13305 }
13306 }
13307
13308 default:
13309 // this is for all other cases trickled down here...
13310 if (!throwException) {
13311 return false;
13312 }
13313 }
添加log,发现是context.checkCallingOrSelfPermission(permission)
的结果在不同线程里不一样,
753 public int checkCallingOrSelfPermission(String permission) {
754 return mBase.checkCallingOrSelfPermission(permission);
755 }
1786 public int checkCallingOrSelfPermission(String permission) {
1787 if (permission == null) {
1788 throw new IllegalArgumentException("permission is null");
1789 }
1790
1791 return checkPermission(permission, Binder.getCallingPid(),
1792 Binder.getCallingUid());
1793 }
这里可以看到,使用了Binder.getCallingPid()和Binder.getCallingUid()作为参数,在碰到的问题中,次线程是通过Binder调起的服务,所以其Binder.getCallingPid()和主线程不一样,是调用者进程的PID,就是这里的差异,导致了在同一个进程中,不同线程的调用结果不一样,
我们可以使用checkSelfPermission(“android.permission.SYSTEM_ALERT_WINDOW”)去判断权限,
或者使用Binder. clearCallingIdentity(); 把调用信息设置为当前进程后再调用Settings.canDrawOverlays方法。
回过头来看,不同线程的执行结果不一样,肯定是有线程差异在影响,我们应当对Binder的线程情况及Binder.getCallingPid有意识。对于Binder线程中权限的获取,我们都应当格外注意。
Settings.canDrawOverlays(context) 方法可以说是埋了个坑,一般不会有这个问题,如果有问题,就用上面的方法去处理。