1. 程式人生 > >Android深入學習之各種隱私許可權判斷和獲取方法總結

Android深入學習之各種隱私許可權判斷和獲取方法總結

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. 在應用中直接開啟許可權申請框,由使用者確定開啟;
  2. 開啟對應許可權的設定頁面,由使用者找到指定應用並勾選;
  3. 開啟應用對應的詳情設定頁面,由使用者找到所需許可權並開啟。

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);
}