• Android PackageManager的妙用


    android.content.pm.PackageManager 用于检索与设备上当前安装的应用程序包相关的各种信息的类。
    要获取应用相关的信息,都用到它。

    通过 Context#getPackageManager 实例化

    获取已安装应用列表

    获取所有已安装的应用,

    private void getAllInstallApp() {
        List list = getPackageManager().getInstalledPackages(0);
        if (null != list && list.size() > 0) {
            Log.d(TAG, " getAllInstallApp -- list.size() : " + list.size());
            for (PackageInfo p : list) {
                Log.d(TAG, "getAllInstallApp -- p.packageName : " + p.packageName);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样写,AS 报黄

    As of Android 11, this method no longer returns information about all apps; see https://g.co/dev/packagevisibility for details
    
    • 1

    Android R(11) 上获取到的应用数量也不全,adb shell pm list package 结果有 189 个,这样获取到的是 74 个(包括系统应用和自己,但不包括安装的第三方应用)。

    在注册文件 AndroidManifest.xml 里添加 后,获取到 189 个了。涉及用户隐私,慎用!!
    AS 也报黄

    A  declaration should generally be used instead of QUERY_ALL_PACKAGES; see https://g.co/dev/packagevisibility for details
    
    • 1

    最终写法

    • 删除
    • 在注册文件 AndroidManifest.xml 里添加 queries 标签,填入需要查询的包名。
    
    
        
            
            
            
        
    
        
    
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    这样就获取到 77 个了(原来的74个 加上 queries里的 3 个)。

    判断应用是否已安装

    可以通过 获取已安装应用列表,然后过滤包名判断,

    也就可以直接判断,

    private boolean isPkgInstall(String pkgName){
        if (null == pkgName || TextUtils.isEmpty(pkgName)) {
            return false;
        }
    
        PackageInfo packageInfo = null;
        try {
            packageInfo = getPackageManager().getPackageInfo(pkgName, 0);
            if (null != packageInfo) {
                return true;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    判断是否是系统应用

    	private boolean isSystemApp(String pkgName) {
            if (null == pkgName || TextUtils.isEmpty(pkgName)) {
                return false;
            }
    
            try {
                PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                    return true;
                }
    
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    判断是否是persist应用

    	private boolean isPersistApp(String pkgName) {
            if (null == pkgName || TextUtils.isEmpty(pkgName)) {
                return false;
            }
    
            try {
                PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
    
                if ((info.applicationInfo.flags &
                        (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT)) ==
                        (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT)) {
                    return true;
                }
    
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    获取应用信息

    通过 PackageInfo 拿到,

    代码说明
    packageInfo.applicationInfo.loadLabel(PackageManager pm)应用名称 ,对应 AndroidManifest.xml 里的 android:label
    packageInfo.applicationInfo.loadIcon(PackageManager pm)Drawable 类型的应用图标
    packageInfo.packageName包名,如 com.android.settings
    packageInfo.versionName版本,如 1.0
    packageInfo.sharedUserId对应 AndroidManifest.xml 里的 android:sharedUserId ,
    Setings 的是 android.uid.system ,
    Bluetooth 的是 com.android.bluetooth ,
    自己写的 Demo 未添加 android:sharedUserId 标签获取到的是 null
    packageInfo.sharedUserLabel获取到的是 0
    packageInfo.lastUpdateTime上次更新时间,是 System.currentTimeMillis() 形式的

    代码示例,

    private void getPkgInfo(String pkgName){
        if (null == pkgName || TextUtils.isEmpty(pkgName)) {
            return;
        }
    
        PackageInfo packageInfo = null;
        PackageManager packageManager = getPackageManager();
        try {
            packageInfo = packageManager.getPackageInfo(pkgName, 0);
            if (null != packageInfo) {
                String label = (String) packageInfo.applicationInfo.loadLabel(packageManager);
                Drawable icon = (Drawable) packageInfo.applicationInfo.loadIcon(packageManager);
                String strPName =  packageInfo.packageName;
                String strVersionName =  packageInfo.versionName;
                String sharedUserId =  packageInfo.sharedUserId;
                int sharedUserLabel =  packageInfo.sharedUserLabel;
                long uTime =  packageInfo.lastUpdateTime;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    获取应用申请的权限

    通过 PackageInfo 获取。

    注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_PERMISSIONS

    private void getPkgPermissionInfo(String pkgName){
        if (null == pkgName || TextUtils.isEmpty(pkgName)) {
            return;
        }
    
        PackageInfo pInfo = null;
        PackageManager packageManager = getPackageManager();
        try {
            pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_PERMISSIONS);
            if (null != pInfo) {
                String[] permissions = pInfo.requestedPermissions;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    打印出的结果就是注册文件 AndroidManifest.xml 里声明的,如
    android.permission.WAKE_LOCK 、android.permission.READ_EXTERNAL_STORAGE 、android.permission.WRITE_EXTERNAL_STORAGE 等,

    模拟器测试,这三个权限我没申请也有 ,
    android.permission.ACCESS_NETWORK_STATE
    android.permission.RECEIVE_BOOT_COMPLETED
    android.permission.FOREGROUND_SERVICE

    获取签名信息

    通过 PackageInfo 获取。

    注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_SIGNATURES

    	try {
                PackageInfo info = getPackageManager().getPackageInfo("com.android.settings", PackageManager.GET_SIGNATURES);
                Signature[] signatures = info.signatures;
                String str = signatures[0].toString();
            } catch (Exception e){
                e.printStackTrace();
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    str 输出为 :android.content.pm.Signature@b2d95fc0

    获取 MD5、 SHA1、 SHA256

    通过 PackageInfo 获取。

    注意,实例化 PackageInfo 时传入的参数是 PackageManager.GET_SIGNATURES

    获取方法是一样的,只是初始化 MessageDigest 时传的值不同,

    	/**
         * 获取 MD5 、SHA1 、SHA256
         * @param algorithm : 只能是 MD5 、SHA1 、SHA256
         * 返回值是这种形式:22:13:F1:91:ED:BC:92:E7:44:1B:34:9E:45:90:AB:76
         * */
        private String getAppSignatureAlgorithm(String pkgName, String algorithm){
            if (null == pkgName || TextUtils.isEmpty(pkgName)) {
                return null;
            }
            try {
                PackageInfo info = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_SIGNATURES);
    
                Signature[] signatures = info.signatures;
                byte[] signBytes = signatures[0].toByteArray();
    
                MessageDigest mDigest = MessageDigest.getInstance(algorithm);
                byte[] bytesMd5 = mDigest.digest(signBytes);
                StringBuilder builder = new StringBuilder();
                for (byte b : bytesMd5) {
                    builder.append(String.format("%02X", b)).append(":");// byte 转 16 进制
                }
    
                builder.replace(builder.length()-1, builder.length(),"");// 删除最后的 :
                return builder.toString();
    
            }catch (Exception e){
                e.printStackTrace();
            }
    
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    使用示例,

    String PKG_SETTINGS = "com.android.settings";
    String sMd5 = getAppSignatureAlgorithm(PKG_SETTINGS, "MD5");
    String sSha1 = getAppSignatureAlgorithm(PKG_SETTINGS, "SHA1");
    String sSha256 = getAppSignatureAlgorithm(PKG_SETTINGS, "SHA256");
    
    • 1
    • 2
    • 3
    • 4

    结果可以参考 AndroidStudio查看apk签名信息_android studio查看apk签名版本-CSDN博客 验证

    判断两个应用签名是否一致

    使用 checkSignatures 方法,源码注释,

    /**
    * Compare the signatures of two packages to determine if the same
    * signature appears in both of them. If they do contain the same
    * signature, then they are allowed special privileges when working
    * with each other: they can share the same user-id, run instrumentation
    * against each other, etc.
    *
    * @param packageName1 First package name whose signature will be compared.
    * @param packageName2 Second package name whose signature will be compared.
    *
    * @return Returns an integer indicating whether all signatures on the
    * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if
    * all signatures match or < 0 if there is not a match ({@link
    * #SIGNATURE_NO_MATCH} or {@link #SIGNATURE_UNKNOWN_PACKAGE}).
    *
    * @see #checkSignatures(int, int)
    */
    @CheckResult
    @SignatureResult
    public abstract int checkSignatures(@NonNull String packageName1,
    @NonNull String packageName2);

    示例,

    int ret = getPackageManager().checkSignatures("com.android.settings","com.test.luodemo");// -3
    
    int ret1 = getPackageManager().checkSignatures("com.android.settings", "com.android.bluetooth");//  0
    
    • 1
    • 2
    • 3

    ret = -3 ,对应 PackageManager.SIGNATURE_NO_MATCH 。

    ret1 = 0 ,对应 PackageManager.SIGNATURE_MATCH 。

    获取四大组件信息

    PackageInfo 源码,获取四大组件需要传对应的 flag ,

        /**
         * Array of all {@link android.R.styleable#AndroidManifestActivity
         * <activity>} tags included under <application>,
         * or null if there were none.  This is only filled in if the flag
         * {@link PackageManager#GET_ACTIVITIES} was set.
         */
        public ActivityInfo[] activities;
    
        /**
         * Array of all {@link android.R.styleable#AndroidManifestReceiver
         * <receiver>} tags included under <application>,
         * or null if there were none.  This is only filled in if the flag
         * {@link PackageManager#GET_RECEIVERS} was set.
         */
        public ActivityInfo[] receivers;
    
        /**
         * Array of all {@link android.R.styleable#AndroidManifestService
         * <service>} tags included under <application>,
         * or null if there were none.  This is only filled in if the flag
         * {@link PackageManager#GET_SERVICES} was set.
         */
        public ServiceInfo[] services;
    
        /**
         * Array of all {@link android.R.styleable#AndroidManifestProvider
         * <provider>} tags included under <application>,
         * or null if there were none.  This is only filled in if the flag
         * {@link PackageManager#GET_PROVIDERS} was set.
         */
        public ProviderInfo[] providers;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    示例代码,
    本例只是拿到 name ,所以用 ComponentInfo
    要拿到启动模式(launchMode)等信息,还是用 ActivityInfoServiceInfoProviderInfo

    	/**
         * @param type : 0 activity , 1 service , 2 provider , 3 receiver
         * */
        private void getPkgComponent(String pkgName, int type){
            if (null == pkgName || TextUtils.isEmpty(pkgName)) {
                return;
            }
    
            PackageInfo pInfo = null;
            PackageManager packageManager = getPackageManager();
            try {
                switch (type){
                    case 1:
                        pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_SERVICES);
                        break;
                    case 2:
                        pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_PROVIDERS);
                        break;
                    case 3:
                        pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_RECEIVERS);
                        break;
                    case 0:
                    default:
                        pInfo = packageManager.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);break;
                }
    
                if (null != pInfo) {
                    ComponentInfo[] componentInfos = null;
                    switch (type){
                        case 1:
                            componentInfos = pInfo.services;
                            break;
                        case 2:
                            componentInfos = pInfo.providers;
                            break;
                        case 3:
                            componentInfos = pInfo.receivers;
                            break;
                        case 0:
                        default:
                            componentInfos = pInfo.activities;
                            break;
                    }
                    if (null != componentInfos && componentInfos.length > 0) {
                        Log.d(TAG, "getPkgComponent -- componentInfos.length : " + componentInfos.length);
                        for (ComponentInfo info : componentInfos) {
                            Log.d(TAG , "getPkgComponent -- info : " + info.name);
                        }
                    }
                }
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    输出结果是这样的 :

    com.test.luodemo.packagemanager.PackageManagerTestActivity
    com.test.luodemo.alarmtest.MyIntentService
    com.test.luodemo.alarmtest.MyWakefulReceiver
    com.test.luodemo.provider.LContentProvider
    
    • 1
    • 2
    • 3
    • 4

    清除应用缓存

    PackageManager.deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) 源码说明,

        /**
         * Attempts to delete the cache files associated with an application.
         * Since this may take a little while, the result will
         * be posted back to the given observer.  A deletion will fail if the calling context
         * lacks the {@link android.Manifest.permission#DELETE_CACHE_FILES} permission, if the
         * named package cannot be found, or if the named package is a "system package".
         *
         * @param packageName The name of the package to delete
         * @param observer An observer callback to get notified when the cache file deletion
         * is complete.
         * {@link android.content.pm.IPackageDataObserver#onRemoveCompleted(String, boolean)}
         * will be called when that happens.  observer may be null to indicate that
         * no callback is desired.
         *
         * @hide
         */
        @SuppressWarnings("HiddenAbstractMethod")
        @UnsupportedAppUsage
        public abstract void deleteApplicationCacheFiles(@NonNull String packageName,
                @Nullable IPackageDataObserver observer);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这个方法使用由限制:

    • 需要 android.permission.DELETE_CACHE_FILES 权限;
    • 只有系统应用才能申请 android.permission.DELETE_CACHE_FILES 权限,需要是系统应用;
    • 标注为 @hide ,无法直接调用。

    使用方法:

    • 1.系统应用直接调用。
      参考安卓原生 Settings 应用的代码,
        public void clearCache() {
            mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_SETTINGS_CLEAR_APP_CACHE);
            mClearCachePreference.setClearingCache(true);
            mPackageManager.deleteApplicationCacheFiles(mEntry.info.packageName,
                    new IPackageDataObserver.Stub() {
                        public void onRemoveCompleted(final String packageName,
                                final boolean succeeded) {
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mClearCachePreference.setClearingCache(false);
                                    cacheCleared(succeeded);
                                }
                            });
                        }
                    });
            mClearCachePreference.refresh();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 2.反射调用。
      AS 编译,导入 framework.jar ,添加依赖,
    compileOnly files('libs/framework.jar')
    
    • 1

    反射调用,mark 下反射的写法

    private final IPackageDataObserver iPackageDataObserver = new IPackageDataObserver.Stub() {
        @Override
        public void onRemoveCompleted(String packageName, boolean isSucceed) throws RemoteException {
    	
        }
    };
    private void clearPkgCacheData(String pkgName){
        if (null == pkgName || TextUtils.isEmpty(pkgName)) {
            return;
        }
    
        PackageManager packageManager = getPackageManager();
    
        Class classPm = null;
        try {
            classPm = Class.forName("android.content.pm.PackageManager");
            Method method = classPm.getDeclaredMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
            method.setAccessible(true);
            method.invoke(packageManager, pkgName, iPackageDataObserver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    不是系统应用,反射可以调用到,但是会报错,

    Caused by: java.lang.SecurityException: Neither user 10156 nor current process has
    	 android.permission.INTERNAL_DELETE_CACHE_FILES.
    
    • 1
    • 2

    设置为系统应用就可以了。

  • 相关阅读:
    《永劫无间》找不到ffmpeg.dll无法继续执行怎么办,永劫无间找不到ffmpeg.dll解决方案
    vue 自定义指令改data中数据
    目标检测YOLO实战应用案例100讲-基于YOLOv5的船舶检测
    日常中出现msvcp140.dll丢失的5个解决方法与msvcp140.dll详细解析
    三种字符串格式化方法(%、format、f-string)
    工业数据采集方案:有何作用和特点?
    CH11_重构API
    内存泄漏检测组件的实现
    pandas使用pd.DateOffset生成时间偏移量、把dataframe数据中的时间数据列统一相加N天M小时、放大、向后偏移N天M小时
    入门opencv,欢笑快乐每一天
  • 原文地址:https://blog.csdn.net/weixin_44021334/article/details/133710929