高通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博客

相关推荐
西瓜本瓜@2 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
似霰5 小时前
安卓adb shell串口基础指令
android·adb
fatiaozhang95278 小时前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO9 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师9 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师9 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫9 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白10 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度
dpxiaolong11 小时前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
tangweiguo0305198712 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android