Android 7.1 AppOpsManagerは、デフォルトでサードパーティアプリケーションのフローティングウィンドウ権限を許可します

プラットホーム

RK3288 + Android 7.1

要求する

SDKの上位バージョンでは、フローティングウィンドウを申請するサードパーティアプリケーションの権限が1ステップ制限されています。
アプリケーションで宣言される権限の申請に加えて、次のようになります。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

また、設定でアクセス許可を開く必要があります。
オプションを開く
アプリケーションは、次のコードを使用してアクセス許可が取得されているかどうかを確認できます。

            AppOpsManager opsMgr = (AppOpsManager)getSystemService(APP_OPS_SERVICE);
            int res = opsMgr.checkOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, Process.myUid(), getPackageName());
            if(res != AppOpsManager.MODE_ALLOWED){
    
    
                showToast(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW + " not allowed");
            }

例外がスローされる場合があります。

java.lang.SecurityException: com.android.myapp from uid 10066 not allowed to perform SYSTEM_ALERT_WINDOW

デフォルトで開く必要がある場合は、関連するコードを変更する必要があります

変更する

フレームワーク/ベース/サービス/コア/java/com/android/server/AppOpsService.java

    private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
    
    
    	//判断是否包含在白名单中, 并将其置为edit 置为 true.
    	//否则, 默认情况下, 则返回空, 导致在应用或其它服务获取packageName时, 发现其并未获取任何操作权限
        edit |= checkIfInWhitelist(packageName);
        UidState uidState = getUidStateLocked(uid, edit);
        if (uidState == null) {
    
    
            return null;
        }
        if (uidState.pkgOps == null) {
    
    
            if (!edit) {
    
    
                return null;
            }
            uidState.pkgOps = new ArrayMap<>();
        }

        Ops ops = uidState.pkgOps.get(packageName);
        if (ops == null) {
    
    
            if (!edit) {
    
    
                return null;
            }
            boolean isPrivileged = false;
            // This is the first time we have seen this package name under this uid,
            // so let's make sure it is valid.
            if (uid != 0) {
    
    
                final long ident = Binder.clearCallingIdentity();
                try {
    
    
                    int pkgUid = -1;
                    try {
    
    
                        ApplicationInfo appInfo = ActivityThread.getPackageManager()
                                .getApplicationInfo(packageName,
                                        PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                                        UserHandle.getUserId(uid));
                        if (appInfo != null) {
    
    
                            pkgUid = appInfo.uid;
                            isPrivileged = (appInfo.privateFlags
                                    & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
                        } else {
    
    
                            if ("media".equals(packageName)) {
    
    
                                pkgUid = Process.MEDIA_UID;
                                isPrivileged = false;
                            } else if ("audioserver".equals(packageName)) {
    
    
                                pkgUid = Process.AUDIOSERVER_UID;
                                isPrivileged = false;
                            } else if ("cameraserver".equals(packageName)) {
    
    
                                pkgUid = Process.CAMERASERVER_UID;
                                isPrivileged = false;
                            }
                        }
                    } catch (RemoteException e) {
    
    
                        Slog.w(TAG, "Could not contact PackageManager", e);
                    }
                    if (pkgUid != uid) {
    
    
                        // Oops!  The package name is not valid for the uid they are calling
                        // under.  Abort.
                        RuntimeException ex = new RuntimeException("here");
                        ex.fillInStackTrace();
                        Slog.w(TAG, "Bad call: specified package " + packageName
                                + " under uid " + uid + " but it is really " + pkgUid, ex);
                        return null;
                    }
                } finally {
    
    
                    Binder.restoreCallingIdentity(ident);
                }
            }
            ops = new Ops(packageName, uidState, isPrivileged);
            if(checkIfInWhitelist(packageName)){
    
    
                //添加默认权限并设置为允许, 这里只加了SYTEM_ALERT_WINDOW
                Op op = new Op(ops.uidState.uid, ops.packageName, AppOpsManager.OP_SYSTEM_ALERT_WINDOW);
                op.mode = AppOpsManager.MODE_ALLOWED;
                ops.put(op.op, op);
            }
            uidState.pkgOps.put(packageName, ops);
        }
        return ops;
    }

    //allow special package for some permission
    //把需要添加默认权限的应用包名加入到白名单中.
    private boolean checkIfInWhitelist(String pkg){
    
    
        if("com.android.testapp".equals(pkg)){
    
    
            return true;
        }
        //....更多应用
        return false;
    }

関連コード

APPのインストール後、権限はデフォルト値、つまり変更されていないデフォルト値になります:
frameworks / base / core / java / android / app / AppOpsManager.java

    public static final int OP_NONE = -1;
	//..........
    /** @hide */
    public static final int OP_WRITE_SETTINGS = 23;
    /** @hide */
    public static final int OP_SYSTEM_ALERT_WINDOW = 24;

    /**
     * This specifies the default mode for each operation.
     */
    private static int[] sOpDefaultMode = new int[] {
    
    
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_IGNORED, // OP_WRITE_SMS
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_DEFAULT, // OP_WRITE_SETTINGS
            AppOpsManager.MODE_DEFAULT, // OP_SYSTEM_ALERT_WINDOW
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
            AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ERRORED,  // OP_MOCK_LOCATION
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,  // OP_TURN_ON_SCREEN
            AppOpsManager.MODE_ALLOWED,
            AppOpsManager.MODE_ALLOWED,  // OP_RUN_IN_BACKGROUND
    };

AppOpsManager.MODE_DEFAULT、// OP_SYSTEM_ALERT_WINDOW

  • 設定
    packages / apps / Settings / src / com / android / settings / applications /AppStateAppOpsBridge.javaでアプリケーションの権限を取得します
    public PermissionState getPermissionInfo(String pkg, int uid) {
    
    
        PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
                .getUserId(uid)));
        try {
    
    
            permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
                    PackageManager.GET_PERMISSIONS | PackageManager.MATCH_UNINSTALLED_PACKAGES,
                    permissionState.userHandle.getIdentifier());
            // Check static permission state (whatever that is declared in package manifest)
            String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
            int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
            if (requestedPermissions != null) {
    
    
                for (int i = 0; i < requestedPermissions.length; i++) {
    
    
                    if (doesAnyPermissionMatch(requestedPermissions[i], mPermissions)) {
    
    
                        permissionState.permissionDeclared = true;
                        if ((permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
    
    
                            permissionState.staticPermissionGranted = true;
                            break;
                        }
                    }
                }
            }
            // Check app op state.
            List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
            if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
    
    
                permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
            }
        } catch (RemoteException e) {
    
    
            Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
        }
        return permissionState;
    }

設定項目が変更されていない場合、mAppOpsManager.getOpsForPackageは空を返します。
変更されている場合は、変更された値を返します。

おすすめ

転載: blog.csdn.net/ansondroider/article/details/106758688