Android深入學習之各種隱私許可權判斷和獲取方法總結
阿新 • • 發佈:2018-12-30
Android深入學習之各種隱私許可權判斷和獲取方法總結
從Android SDK 23 開始, Android就改變了許可權的管理模式。對於一些涉及使用者隱私的許可權則需要使用者的授權才可以使用。在此之前,開發者只需要在AndroidManifest.xml中註冊,如網路許可權、wifi許可權等等:許可權即可,但是現在除了註冊還需要進行手動的授權。
沒有涉及使用者隱私的許可權申請的方式比較簡單,即只需要在AndroidManifest.xml中註冊,如網路許可權、wifi許可權等等:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
本文主要研究涉及使用者隱私許可權的判斷和申請方法,根據需要使用者的操作不同,將這類許可權分為三類:
- 在應用中直接開啟許可權申請框,由使用者確定開啟;
- 開啟對應許可權的設定頁面,由使用者找到指定應用並勾選;
- 開啟應用對應的詳情設定頁面,由使用者找到所需許可權並開啟。
1.在應用中直接開啟許可權申請框,由使用者確定
以申請手機識別碼和申請讀寫手機儲存許可權為例,具體程式碼如下:
boolean hasPhoneStatePermission = false; boolean hasWriteStorePermission = false; // 判斷是否具有某許可權的方法ContextCompat.checkSelfPermission() if (ContextCompat.checkSelfPermission()(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { hasPhoneStatePermission = true; } if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { hasWriteStorePermission = true; } if (hasPhoneStatePermission && hasWriteStorePermission) { return; } // 可一次申請多個許可權的方法ActivityCompat.requestPermissions(),會依次彈出對應的許可權彈框 ActivityCompat.requestPermissions(this, { Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, CODE);
PS:許可權必須在AndroidMainifest.xml裡面宣告,否則不生效:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2.開啟對應許可權的設定頁面,由使用者找到指定應用再勾選
首頁需要判斷是否已具有要申請的許可權,不同的許可權判斷方法不同,需要單獨區分許可權,本文羅列幾個常用許可權通知欄讀取許可權、無障礙許可權以及懸浮窗許可權的判斷方法:
是否具有通知欄讀取許可權判斷方法:
public static boolean isNoticationEnabled(String packageName) {
String allowedInfo = Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
if (allowedInfo == null || !allowedInfo.contains(packageName)) {
return false;
} else {
return true;
}
}
是否具有無障礙許可權判斷方法:
public static boolean isAccessibilityServiceEnabled(String packageName) {
AccessibilityManager accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo info : accessibilityServices) {
if (info != null && info.getId().contains(packageName)) {
Toast.makeText(this, "恭喜你,你的應用已具有無障礙許可權!!!", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
是否具有懸浮窗許可權判斷方法:
public static boolean checkAlertWindowsPermission(Context context) {
try {
Object object = context.getSystemService(Context.APP_OPS_SERVICE);
if (object == null) {
return false;
}
Class localClass = object.getClass();
Class[] arrayOfClass = new Class[3];
arrayOfClass[0] = Integer.TYPE;
arrayOfClass[1] = Integer.TYPE;
arrayOfClass[2] = String.class;
Method method = localClass.getMethod("checkOp", arrayOfClass);
if (method == null) {
return false;
}
Object[] arrayOfObject1 = new Object[3];
arrayOfObject1[0] = 24;
arrayOfObject1[1] = Binder.getCallingUid();
arrayOfObject1[2] = context.getPackageName();
int m = ((Integer) method.invoke(object, arrayOfObject1));
return m == AppOpsManager.MODE_ALLOWED;
} catch (Exception ex) {
}
return false;
}
在判斷沒有需要申請的許可權的情況下,需要跳轉到對應許可權開啟頁面,由使用者手動開啟,本文羅列幾個常用許可權通知欄讀取許可權、無障礙許可權以及懸浮窗許可權的開啟方法:
通知欄讀取許可權介面開啟方法:
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
無障礙許可權介面開啟方法:
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
懸浮窗許可權介面開啟方法:
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + packageName)); // packageName為對應應用包名
startActivity(intent);
3.開啟應用對應的詳情設定頁面,開啟需要申請的許可權
首頁需要判斷是否已具有要申請的許可權,這個類別的以通知欄顯示許可權的判斷方法為例:
是否具有通知欄顯示許可權判斷方法:
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
public static boolean areNotificationsEnabled(Context context) {
boolean flag = true;
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= 24) {
try {
Class cls = Class.forName("android.app.NotificationManager");
Method method = cls.getDeclaredMethod("areNotificationsEnabled");
method.setAccessible(true);
flag = (boolean) method.invoke(manager,new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
} else if (Build.VERSION.SDK_INT >= 19) {
Object appOps = context.getSystemService("appops");
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
try {
Class<?> appOpsClass = Class.forName("android.app.AppOpsManager");
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE,
Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int) opPostNotificationValue.get(Integer.class);
int v = (int) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg);
return v == 0;
} catch (Exception e) {
return true;
}
}
return flag;
}
開啟應用對應的詳情設定頁面:
private void toSettingDetail() {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package", Constants.PACKAGE_NAME, null));
} else if (Build.VERSION.SDK_INT <= 8) {
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails");
intent.putExtra("com.android.settings.ApplicationPkgName", packageName); // packageName為對應應用包名
}
startActivity(intent);
}