高通Android 12/13冻结应用

最近开发SDK遇到冻结应用需求,于是简单记录下。总体而言比较简单,调用系统接口实现此功能。

涉及类与方法

IPackageManager .aidl #  * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}.
     */
    @UnsupportedAppUsage
    void setApplicationEnabledSetting(in String packageName, in int newState, int flags,
            int userId, String callingPackage);

PackageManager.java # setApplicationEnabledSetting



PackageManagerService.java  # setApplicationEnabledSetting # setEnabledSetting

1、IPackageManager.aidl

代码路径 /frameworks/base/core/java/android/content/pm/IPackageManager.aidl

/**
     * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}.
     */
    @UnsupportedAppUsage
    void setApplicationEnabledSetting(in String packageName, in int newState, int flags,
            int userId, String callingPackage);

2、PackageManagerService.java

代码路径 /frameworks/base/core/java/android/content/pm/PackageManagerService.java

 @Override
    public void setComponentEnabledSetting(ComponentName componentName,
            int newState, int flags, int userId) {
        if (!mUserManager.exists(userId)) return;
        setEnabledSetting(componentName.getPackageName(),
                componentName.getClassName(), newState, flags, userId, null);
    }

    private void setEnabledSetting(final String packageName, String className, int newState,
            final int flags, int userId, String callingPackage) {
        if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
              || newState == COMPONENT_ENABLED_STATE_ENABLED
              || newState == COMPONENT_ENABLED_STATE_DISABLED
              || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
              || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
            throw new IllegalArgumentException("Invalid new component state: "
                    + newState);
        }
        PackageSetting pkgSetting;
        final int callingUid = Binder.getCallingUid();
        final int permission;
        if (callingUid == Process.SYSTEM_UID) {
            permission = PackageManager.PERMISSION_GRANTED;
        } else {
            permission = mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
        }
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                true /* checkShell */, "set enabled");
        final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
        boolean sendNow = false;
        boolean isApp = (className == null);
        final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
        String componentName = isApp ? packageName : className;
        ArrayList<String> components;

        // reader
        synchronized (mLock) {
            pkgSetting = mSettings.getPackageLPr(packageName);
            if (pkgSetting == null) {
                if (!isCallerInstantApp) {
                    if (className == null) {
                        throw new IllegalArgumentException("Unknown package: " + packageName);
                    }
                    throw new IllegalArgumentException(
                            "Unknown component: " + packageName + "/" + className);
                } else {
                    // throw SecurityException to prevent leaking package information
                    throw new SecurityException(
                            "Attempt to change component state; "
                            + "pid=" + Binder.getCallingPid()
                            + ", uid=" + callingUid
                            + (className == null
                                    ? ", package=" + packageName
                                    : ", component=" + packageName + "/" + className));
                }
            }
        }

        // Limit who can change which apps
        if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
            // Don't allow apps that don't have permission to modify other apps
            final boolean filterApp;
            synchronized (mLock) {
                filterApp = (!allowedByPermission
                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
            }
            if (filterApp) {
                throw new SecurityException(
                        "Attempt to change component state; "
                                + "pid=" + Binder.getCallingPid()
                                + ", uid=" + callingUid
                                + (className == null
                                ? ", package=" + packageName
                                        : ", component=" + packageName + "/" + className));
            }
            // Don't allow changing protected packages.
            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
                throw new SecurityException("Cannot disable a protected package: " + packageName);
            }
        }
        // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
        // app details activity
        if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
                && !allowedByPermission) {
            throw new SecurityException("Cannot disable a system-generated component");
        }

        synchronized (mLock) {
            if (callingUid == Process.SHELL_UID
                    && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
                // Shell can only change whole packages between ENABLED and DISABLED_USER states
                // unless it is a test package.
                int oldState = pkgSetting.getEnabled(userId);
                if (className == null
                        &&
                        (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
                                || oldState == COMPONENT_ENABLED_STATE_DEFAULT
                                || oldState == COMPONENT_ENABLED_STATE_ENABLED)
                        &&
                        (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                                || newState == COMPONENT_ENABLED_STATE_DEFAULT
                                || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
                    // ok
                } else {
                    throw new SecurityException(
                            "Shell cannot change component state for " + packageName + "/"
                                    + className + " to " + newState);
                }
            }
        }
        if (className == null) {
            // We're dealing with an application/package level state change
            synchronized (mLock) {
                if (pkgSetting.getEnabled(userId) == newState) {
                    // Nothing to do
                    return;
                }
            }
            // If we're enabling a system stub, there's a little more work to do.
            // Prior to enabling the package, we need to decompress the APK(s) to the
            // data partition and then replace the version on the system partition.
            final AndroidPackage deletedPkg = pkgSetting.pkg;
            final boolean isSystemStub = (deletedPkg != null)
                    && deletedPkg.isStub()
                    && deletedPkg.isSystem();
            if (isSystemStub
                    && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                            || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
                if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
                    return;
                }
            }
            if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
                // Don't care about who enables an app.
                callingPackage = null;
            }
            synchronized (mLock) {
                pkgSetting.setEnabled(newState, userId, callingPackage);
                if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                        || newState == COMPONENT_ENABLED_STATE_DISABLED)
                        && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
                        == PERMISSION_GRANTED) {
                    // This app should not generally be allowed to get disabled by the UI, but if it
                    // ever does, we don't want to end up with some of the user's apps permanently
                    // suspended.
                    unsuspendForSuspendingPackage(packageName, userId);
                    removeAllDistractingPackageRestrictions(userId);
                }
            }
        } else {
            synchronized (mLock) {
                // We're dealing with a component level state change
                // First, verify that this is a valid class name.
                AndroidPackage pkg = pkgSetting.pkg;
                if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
                    if (pkg != null &&
                            pkg.getTargetSdkVersion() >=
                                    Build.VERSION_CODES.JELLY_BEAN) {
                        throw new IllegalArgumentException("Component class " + className
                                + " does not exist in " + packageName);
                    } else {
                        Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
                                + className + " does not exist in " + packageName);
                        // Safetynet logging for b/240936919
                        EventLog.writeEvent(0x534e4554, "240936919", callingUid);
                        return;
                    }
                }
                switch (newState) {
                    case COMPONENT_ENABLED_STATE_ENABLED:
                        if (!pkgSetting.enableComponentLPw(className, userId)) {
                            return;
                        }
                        break;
                    case COMPONENT_ENABLED_STATE_DISABLED:
                        if (!pkgSetting.disableComponentLPw(className, userId)) {
                            return;
                        }
                        break;
                    case COMPONENT_ENABLED_STATE_DEFAULT:
                        if (!pkgSetting.restoreComponentLPw(className, userId)) {
                            return;
                        }
                        break;
                    default:
                        Slog.e(TAG, "Invalid new component state: " + newState);
                        return;
                }
            }
        }
        synchronized (mLock) {
            if ((flags & PackageManager.SYNCHRONOUS) != 0) {
                flushPackageRestrictionsAsUserInternalLocked(userId);
            } else {
                scheduleWritePackageRestrictionsLocked(userId);
            }
            updateSequenceNumberLP(pkgSetting, new int[] { userId });
            final long callingId = Binder.clearCallingIdentity();
            try {
                updateInstantAppInstallerLocked(packageName);
            } finally {
                Binder.restoreCallingIdentity(callingId);
            }
            components = mPendingBroadcasts.get(userId, packageName);
            final boolean newPackage = components == null;
            if (newPackage) {
                components = new ArrayList<>();
            }
            if (!components.contains(componentName)) {
                components.add(componentName);
            }
            if ((flags&PackageManager.DONT_KILL_APP) == 0) {
                sendNow = true;
                // Purge entry from pending broadcast list if another one exists already
                // since we are sending one right away.
                mPendingBroadcasts.remove(userId, packageName);
            } else {
                if (newPackage) {
                    mPendingBroadcasts.put(userId, packageName, components);
                }
                if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
                    // Schedule a message - if it has been a "reasonably long time" since the
                    // service started, send the broadcast with a delay of one second to avoid
                    // delayed reactions from the receiver, else keep the default ten second delay
                    // to avoid extreme thrashing on service startup.
                    final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay
                                                ? BROADCAST_DELAY
                                                : BROADCAST_DELAY_DURING_STARTUP;
                    mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay);
                }
            }
        }

        final long callingId = Binder.clearCallingIdentity();
        try {
            if (sendNow) {
                int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                sendPackageChangedBroadcast(packageName,
                        (flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

3、PackageManager.java

代码路径 /frameworks/base/core/java/android/content/pm/PackageManager.java

     * Set the enabled setting for an application
     * This setting will override any enabled state which may have been set by the application in
     * its manifest.  It also overrides the enabled state set in the manifest for any of the
     * application's components.  It does not override any enabled state set by
     * {@link #setComponentEnabledSetting} for any of the application's components.
     *
     * @param packageName The package name of the application to enable
     * @param newState The new enabled state for the application.
     * @param flags Optional behavior flags.
     */
    @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
            conditional = true)
    public abstract void setApplicationEnabledSetting(@NonNull String packageName,
            @EnabledState int newState, @EnabledFlags int flags);

4、代码调用 示例 packageName就是当前需要冻结应用包名 冻结标志位PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER

IPackageManager iPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
iPackageManager.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLE, 0, 0, "shell:" + android.os.Process.myUid());

5、测试之后在Settings里面查看冻结应用是否显示停用,如果是表面冻结应用 成功。解冻也是类似只是设置标志位不一样, PackageManager.COMPONENT_ENABLED_STATE_ENABLED

try {
            IPackageManager iPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
            if (iPackageManager != null) {
                iPackageManager.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                        0, 0, "shell:" + android.os.Process.myUid());
                Log.e("unfreezeApp", "freeze PackageName=" + packageName);
            }
        } catch (RemoteException e) {
            Log.e("unfreezeApp", "freeze PackageManager RemoteException=" + e.getMessage());
        }

到这里基本结束了。转载请注册出处高通Android 12/13冻结应用-CSDN博客,谢谢!

补充标志位状态值

COMPONENT_ENABLED_STATE_DEFAULT, //启用APP或组件,忽略manifest的定义。 COMPONENT_ENABLED_STATE_ENABLED, //禁用APP或组件,忽略manifest的定义。 COMPONENT_ENABLED_STATE_DISABLED, //以用户身份禁用APP,忽略manifest的定义。不能用于组件操作。 COMPONENT_ENABLED_STATE_DISABLED_USER, //禁用APP直到用户想用才出现。也就是说,正常情况下,用户看不到(比如在Launcher上);但是特殊情况下,用户还是能看到并选择到(比如输入法APP)。不能用于组件操作。 COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,

感谢 Android 获取所有被禁用或冷冻的应用信息_android获取被停用的应用-CSDN博客

相关推荐
天空中的野鸟1 小时前
Android音频采集
android·音视频
小白也想学C2 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程2 小时前
初级数据结构——树
android·java·数据结构
闲暇部落4 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX6 小时前
Android 分区相关介绍
android
大白要努力!7 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee7 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood8 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-10 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen13 小时前
MTK Android12 user版本MtkLogger
android·framework