AndroidはFrameworkのデフォルトでlauncher runtime権限を付与します

55845 ワード

Androidバージョン:8.1問題説明:launcherで通話記録callogや未読メールなどの内容を表示するには、実行時権限に関連し、実行時権限を申請しなければならないため、ポップアップが提示され、お客様のニーズはもちろんポップアップは許されません.最初の起動では、ライセンスを黙認しなければなりません.
一、privapp-permissions-mediatek.xmlが無効です
以前、このような権限の問題をしたのはprivapp-permissions権限のホワイトリストに直接対応権限を加えたのを覚えていますが、現在のプロジェクトではサポートされておらず、具体的な原因が見つからず、署名の違いによるものと推測されていますが、具体的な原因は、ここでは疑問符を打つしかありません.とにかく権限ホワイトリストが失効しました.
二、context.checkSelfPermission()の流れ
前のホワイトリストにはすでに書いてありましたが、checkSelfPermissionの時にdenyに戻ったので、checkSelfPermissionの検出過程を探し始めました.
Context.java
    @PackageManager.PermissionResult
    public abstract int checkSelfPermission(@NonNull String permission);

checkSelfPermissionは抽象的な方法であり,自然とその実装者はContextImplである.
ContextImpl.java
    @Override
    public int checkSelfPermission(String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return checkPermission(permission, Process.myPid(), Process.myUid());
    }
@Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }
        final IActivityManager am = ActivityManager.getService();
        if (am == null) {
            // Well this is super awkward; we somehow don't have an active
            // ActivityManager instance. If we're testing a root or system
            // UID, then they totally have whatever permission this is.
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

ここではIActivityManagerインタフェースで処理します.もちろんIActivityManagerインタフェースを実現するのはエンティティクラスです.ActivityManagerService extends IActivityManager.Stub
ActivityManagerServiceでcheckPermission
    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }
    int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

続いてActivity Managerに入りました
/** @hide */
    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

前は特殊な状況を判断しています.例えば
  • appId == Process.ROOT_UIDとはappIdがrootユーザーに属して直接PERMISSIONに戻るという意味です.GRANTED
  • exported==false、つまりこのコンポーネントcontentprovider、またはactivityがexported falseを設定すると、PERMISSION_に直接戻ります.DENIED

  • 他の一般的な状況はAppGlobalsに入ることですgetPackageManager() .checkUidPermission
    ここで取得したのはPackagemanagerインタフェースです
        public static IPackageManager getPackageManager() {
            return ActivityThread.getPackageManager();
        }
    

    実際のオブジェクトはPackageManagerServiceです
    @Override
        public int checkUidPermission(String permName, int uid) {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
            final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
            final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
            final int userId = UserHandle.getUserId(uid);
            if (!sUserManager.exists(userId)) {
                return PackageManager.PERMISSION_DENIED;
            }
            /// M: CTA requirement - permission control  @{
            sCtaManager.reportPermRequestUsage(permName, uid);
            //@}
            synchronized (mPackages) {
                Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
                if (obj != null) {
                    if (obj instanceof SharedUserSetting) {
                        if (isCallerInstantApp) {
                            return PackageManager.PERMISSION_DENIED;
                        }
                    } else if (obj instanceof PackageSetting) {
                        final PackageSetting ps = (PackageSetting) obj;
                        if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
                            return PackageManager.PERMISSION_DENIED;
                        }
                    }
                    final SettingBase settingBase = (SettingBase) obj;
                    final PermissionsState permissionsState = settingBase.getPermissionsState();
                    if (permissionsState.hasPermission(permName, userId)) {
                        if (isUidInstantApp) {
                            BasePermission bp = mSettings.mPermissions.get(permName);
                            if (bp != null && bp.isInstant()) {
                                return PackageManager.PERMISSION_GRANTED;
                            }
                        } else {
                            return PackageManager.PERMISSION_GRANTED;
                        }
                    }
                    // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
                    if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
                            .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                } else {
                    ArraySet<String> perms = mSystemPermissions.get(uid);
                    if (perms != null) {
                        if (perms.contains(permName)) {
                            return PackageManager.PERMISSION_GRANTED;
                        }
                        if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
                                .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
                            return PackageManager.PERMISSION_GRANTED;
                        }
                    }
                }
            }
    
            return PackageManager.PERMISSION_DENIED;
        }
    

    この方法は重要で、結果はここで判断したので、印刷によってわかります.getUserIdLPrはlauncher idに対応するobjectが空ではないことを返し、obj!=null検出結果はpermissionsStateを示す.hasPermission(permName,userId)==falseこれは、launcherのPermissionsStateに必要な実行権限が含まれていないことを示しています.
    しかしこのpermissionsStateはシステムが初期化されたときに対応しているので、初期化されたときにこの付与権限がまったくありません.
    三、システムの初期化からなぜ自動授権がないのかを探す
    システムの初期化が完了すると、PackageManagerServiceもシステムReadyを呼び出すのは当然です.
    PackageManagerService–systemReady()
        @Override
        public void systemReady() {
    		...
            // If we upgraded grant all default permissions before kicking off.
            for (int userId : grantPermissionsUserIds) {
                mDefaultPermissionPolicy.grantDefaultPermissions(userId);
            }
            ...
       }
    

    ここにはデフォルトのライセンスがあります.
        public void grantDefaultPermissions(int userId) {
            if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
                grantAllRuntimePermissions(userId);
            } else {
                grantPermissionsToSysComponentsAndPrivApps(userId);
                grantDefaultSystemHandlerPermissions(userId);
                grantDefaultPermissionExceptions(userId);
            }
        }
    

    ここでPackageManager.FEATURE_EMBEDDEDはサポートされていないので、次の3つの方法を歩いて、最初の方法に重点を置きます.
    このメソッド名はシステムコンポーネントと特権appに許可されているのではないでしょうか.しかしなぜlauncherはpriv appにあるのに、権限がないのですか?
        private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
            Log.i(TAG, "Granting permissions to platform components for user " + userId);
    
            synchronized (mService.mPackages) {
                for (PackageParser.Package pkg : mService.mPackages.values()) {
                    if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
                            || !doesPackageSupportRuntimePermissions(pkg)
                            || pkg.requestedPermissions.isEmpty()) {
                        continue;
                    }
                    grantRuntimePermissionsForPackageLocked(userId, pkg);
                }
            }
        }
    

    システムpackagesを巡って、3つの判断をして、判断を通じてやっと歩いていくことができます
  • isSysComponentOrPersistentPlatformSignedPrivAppLPr
  • doesPackageSupportRuntimePermissions
  • pkg.requestedPermissions.isEmpty()

  • ここで最初の方法は,そのコンポーネントがシステムコンポーネントであるか,Persistentのシステム署名であるかを判断するappである.
    private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
            if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
                return true;
            }
            if (!pkg.isPrivilegedApp()) {
                return false;
            }
            PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
            if (sysPkg != null && sysPkg.pkg != null) {
                if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
                    return false;
                }
            } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
                return false;
            }
            return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
                    pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
        }
    

    laucnherにとってpkg.isPrivilegedApp()はtrueに違いないが、ここのApplicationInfo.FLAG_PERSISTENT、launcherはこの属性がなくて、それから後ろはシステムの署名のマッチングで、pkg.mSignatures、つまりLOCAL_CERTIFICATE:=platform、launcherも設定されていません.だからここで戻るのはfalseに違いない.
    2つ目は、パッケージのtargetSdkValersonが22より大きいかどうか、つまりandroid 6.0であるかどうかを確認します.
        private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
            return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
        }
    

    第三項、pkg.requestedPermissions.isEmpty()権限リストが空かどうか、
    条件判断によると、ここで必ず3つの検査が返されるのはfalseでなければならない.つまり、システム署名でFLAG_でなければならない.PERSISTENTのappでtargetsdk>22でpermissionsが空でない場合、continueは次のpackageにジャンプします.
    条件を満たすappだけが後ろのgrantRuntimePermissionsForPackageLockedに進む
        private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
            Set<String> permissions = new ArraySet<>();
            for (String permission :  pkg.requestedPermissions) {
                BasePermission bp = mService.mSettings.mPermissions.get(permission);
                if (bp != null && bp.isRuntime()) {
                    permissions.add(permission);
                }
            }
            if (!permissions.isEmpty()) {
                grantRuntimePermissionsLPw(pkg, permissions, true, userId);
            }
        }
    
        private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
                boolean systemFixed, int userId) {
            grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId);
        }
    

    次にやったのはライセンスです
        private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
                boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
    		......
    		mService.grantRuntimePermission(pkg.packageName, permission, userId);
    		......
    		  
    
    private void grantRuntimePermission(String packageName, String name, final int userId,
                boolean overridePolicy) {
            ......
    		final int result = permissionsState.grantRuntimePermission(bp, userId);
    		......
    }
    

    つまり前述のpermissionsStateには対応権限が与えられており、
        public int grantRuntimePermission(BasePermission permission, int userId) {
            enforceValidUserId(userId);
            if (userId == UserHandle.USER_ALL) {
                return PERMISSION_OPERATION_FAILURE;
            }
            return grantPermission(permission, userId);
        }
    

    プロセスはこのようにして、後で見ないで、launcherの問題に対して、前の判断の中で出て、印刷は知っていて、launcherパッケージが判断してシステムのコンポーネントがあるかどうかを検査する時falseに戻って、これはシステムの初期化の時にlauncherの運行権限を自動的に授権することはできません.肝心なところは
    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0
    
    PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
                    pkg.mSignatures) == PackageManager.SIGNATURE_MATCH
    

    この2つは満たさなければならないので、ラベルに追加します.
    android:persistent="true"
    

    makefileに追加
    LOCAL_CERTIFICATE := platform
    

    このようにしてこそ、前の条件を満たすことができる.
    再コンパイル、launcher再チェック権限は、通話関連権限が許可されていることを返します.デフォルトruntimeライセンスが解決されました.
    int result = mcontext.checkSelfPermission("android.permission.READ_PHONE_STATE")
    result = PackageManager.PERMISSION_GRANTED
    

    実は開発にとって、もっと乱暴な方法は、launcherパッケージ名を直接判断してtrueに戻ることですが、これはあまりにも乱暴です.
    if (pkg.packageName.contains("launcher3")) {
                return true;
    }
    

    まとめ:
    実行権限の問題で、いくつかの場所で権限を正しく取得できず、ポップアップ申請も許可されていないため、デフォルトの権限が必要になります.これは必ずソースコードを歩かなければなりません.権限検査の流れを歩くことで、権限というブロックに対する理解を深めました.後でこのような問題があったら,直接行った.