Android9.0 如何實現動態權限白名單

前言

此次使用Android9.0做一個動態權限白名單的功能,即客戶要求他們的app默認就該應用所有的權限,在運行過程中,不需要去動態申請權限,即不彈窗。

在Android M之前, Runtime permissions是直接被當作是install permissons,即在安裝的時候就直接grant了。

而在 Android M以後, Android加入了runtime permissions, 也就是dangerous permissons, 這些權限有可能會刺探用戶隱私等等危害。

這樣系統在安裝APP的時候就不會默認grant runtime permissions.

修改思路

申請時,拒絕申請

一開始想着在應用提出動態申請的請求時,就將該請求掐斷,這樣就不會彈窗出來,即在如下方法中直接返回:

frameworks/base/core/java/android/app/Activity.java
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    
    return;  //在這裏直接return
    if (requestCode < 0) {
        throw new IllegalArgumentException("requestCode should be >= 0");
    }
    if (mHasCurrentPermissionsRequest) {
        Log.w(TAG, "Can request only one set of permissions at a time");
        // Dispatch the callback with empty arrays which means a cancellation.
        onRequestPermissionsResult(requestCode, new String[0], new int[0]);
        return;
    }
    Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

這樣做,確實不會彈窗,但其實應用並沒有獲取到想要的動態權限,此路不通。

在check權限時直接返回授權

這裏是在檢測app權限的時候,直接給app返回授權,但其實這裏也沒有真正的授權,是一種假授權。

從update permission入手

對Android來說,既然有動態權限的申請,那麼就會有一個地方用於處理權限更新的地方,這次決定從這裏入手。

通過強大的grep指令,在frameworks目錄下進行搜索,最終定位到updatePermission方法中,該方法用於更新應用權限,該方法最終調用了grantPermissions方法,源碼如下:

//這裏由於該方法太長,只貼出部分的上下文代碼
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void grantPermissions(PackageParser.Package pkg, boolean replace,
    String packageOfInterest, PermissionCallback callback) {
    ......
    synchronized (mLock) {
    final int N = pkg.requestedPermissions.size();
    for (int i = 0; i < N; i++) {
        final String permName = pkg.requestedPermissions.get(i);
        final BasePermission bp = mSettings.getPermissionLocked(permName);
        final boolean appSupportsRuntimePermissions =
                pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;

        if (DEBUG_INSTALL) {
            Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
        }

        if (bp == null || bp.getSourcePackageSetting() == null) {
            if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                if (DEBUG_PERMISSIONS) {
                    Slog.i(TAG, "Unknown permission " + permName
                            + " in package " + pkg.packageName);
                }
            }
            continue;
        }

        // Limit ephemeral apps to ephemeral allowed permissions.
        if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
            if (DEBUG_PERMISSIONS) {
                Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
                        + " for package " + pkg.packageName);
            }
            continue;
        }

        if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
            if (DEBUG_PERMISSIONS) {
                Log.i(TAG, "Denying runtime-only permission " + bp.getName()
                        + " for package " + pkg.packageName);
            }
            continue;
        }

        final String perm = bp.getName();
        boolean allowedSig = false;
        int grant = GRANT_DENIED;

        // Keep track of app op permissions.
        if (bp.isAppOp()) {
            mSettings.addAppOpPackage(perm, pkg.packageName);
        }

        if (bp.isNormal()) {
            // For all apps normal permissions are install time ones.
            grant = GRANT_INSTALL;
        } else if (bp.isRuntime()) {
            // If a permission review is required for legacy apps we represent
            // their permissions as always granted runtime ones since we need
            // to keep the review required permission flag per user while an
            // install permission's state is shared across all users.
            if (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
                // For legacy apps dangerous permissions are install time ones.
                grant = GRANT_INSTALL;
            } else if (origPermissions.hasInstallPermission(bp.getName())) {
                // For legacy apps that became modern, install becomes runtime.
                grant = GRANT_UPGRADE;
            } else if (isLegacySystemApp) {
                // For legacy system apps, install becomes runtime.
                // We cannot check hasInstallPermission() for system apps since those
                // permissions were granted implicitly and not persisted pre-M.
                grant = GRANT_UPGRADE;
            } else {
                / For modern apps keep runtime permissions unchanged.
                //permission check whitelist app @{
                if (!isneedCheckPermission(pkg.packageName)) {
                    grant = GRANT_INSTALL;
                } else {
                    grant = GRANT_RUNTIME;
                }
                //permission check whitelist app @}
            }
        } else if (bp.isSignature()) {
            // For all apps signature permissions are install time ones.
            allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
            if (allowedSig) {
                grant = GRANT_INSTALL;
            }
        }

        ......

}

在Android6.0之前是沒有動態權限這個概念的,只有在Android6.0之後纔有。在Android6.0之前,對於權限,都將其進行安裝,不需要進行GRANT_RUNTIME,統一都是GRANT_INSTALL,而到了Android6.0之後,則多出了一個GRANT_RUNTIME,即運行時權限,如果是normal級別的權限,則會apk安裝的過程中,就直接授權,但如果是危險級別的,涉及到用戶隱祕的,則需要在調用的時候,彈窗讓用戶選擇是否授權,也就是所謂的GRANT_RUNTIME。

所以我們的方法很簡單,就是找到GRANT_RUNTIME ,然後使用GRANT_INSTALL代替即可。

白名單

這裏還涉及到一個白名單配置方法,主要是新增一個cfg配置文件,文件內容是每一個應用的包名,這裏的包名可以不完整,但一定要有明確的關鍵詞,避免跟其他的應用混淆。該方法如下:

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private boolean isneedCheckPermission(String packagename) {
    ArrayList<String> whiteListApp = new ArrayList<String>();

    try {
        BufferedReader br = new BufferedReader(new java.io.InputStreamReader(
            new FileInputStream("/system/etc/WhiteListPermissionFilter.cfg")));

        String line = "";
        while ((line = br.readLine()) != null) {
            whiteListApp.add(line);
        }

        br.close();
    } catch (java.io.FileNotFoundException ex) {
        Log.d(TAG, "WhiteListPermissionFilter.cfg - FileNotFoundException");
        return true;
    } catch(java.io.IOException ex) {
        Log.d(TAG, "WhiteListPermissionFilter.cfg - IOException");
        return true;
    }

    Iterator<String> it = whiteListApp.iterator();

    while (it.hasNext()) {
        String whitelistItem = it.next();
        Log.d(TAG, "whitelistItem:" + whitelistItem);
        if (packagename.contains(whitelistItem)) {
            return false;
        }
    }
    return true;
}

最後

若想關注更多Android系統開發相關文章,可掃碼關注如下微信公衆號。
在這裏插入圖片描述

發佈了19 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章