Android 中要禁用 APP四大组件,可使用PackageManager提供的方法:

/** * 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. */public abstract void setApplicationEnabledSetting(String packageName,        @EnabledState int newState, @EnabledFlags int flags);/** * Set the enabled setting for a package component (activity, receiver, service, provider). * This setting will override any enabled state which may have been set by the component in its * manifest. * * @param componentName The component to enable * @param newState The new enabled state for the component. * @param flags Optional behavior flags. */public abstract void setComponentEnabledSetting(ComponentName componentName,        @EnabledState int newState, @EnabledFlags int flags);

要想使用这两个 API 得先获取权限。

权限获取

获取权限又有几种情况几个步骤,如下:

1、在 Manifest 里面声明权限:
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
2、使用系统签名

在申明上面的权限时,AS 会提示:

Permission is only granted to system apps less... (Ctrl+F1) Permissions with the protection level signature, privileged or signatureOrSystem are only granted to system apps. If an app is a regular non-system app, it will never be able to use these permissions.

这就是说,这个权限只有系统 APP 可以获取到,这时就需要使用系统签名来签名 APP 了。

如何给 APP 系统签名,方法有多种,比如:
1、APP 源码放在系统源码中编译;
2、APK 文件放在系统源码中重签名;
3、导出系统签名文件,给 APK 签名;
4、使用系统签名,制作 keystore,放入 AS 使用。
相关方法,网上文章很多,我也在公众号推送过我自己的方法,有兴趣可以去查阅。

3、添加 sharedUserId

如果你还想在当前 APP 中去操作其他 APP,在上面都配置好的前提下,还可能会出现类似如下的错误信息:

java.lang.SecurityException: Permission Denial: attempt to change component state from pid=27978, uid=10111, package uid=10087

这时,就需要再给 APP 添加 sharedUserId,让 APP 运行在 system 进程中:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    android:sharedUserId="android.uid.system">

实际添加了 “android.uid.system” 后,上一步的 uses-permission 可以省略,因为 system 进程默认有这个权限。

添加好权限后,接下来就是使用了。

既然是 PackageManager 的方法,而且这两个方法也不是静态的,那就要先获取 PackageManager 的实例了。这两个方法还是 abstract 的,那就说明 PackageManager 也是 abstract 的,不能 new ,那如何获取实例呢?

我们知道系统很多 Manager 都是单实例不能 new 的,PackageManager 也一样,那是不是也是通过 getSystemService 获取呢?并不是,注释给出来了:

/** * Class for retrieving various kinds of information related to the application * packages that are currently installed on the device. * * You can find this class through {@link Context#getPackageManager}. */public abstract class PackageManager {

知道如何获取实例之后,我们就可以开始调用了,先看如何禁用 APP。

禁用 APP

在开始操作前,我们应该先了解下这个方法及其参数定义和作用:

setApplicationEnabledSetting(String packageName,        @EnabledState int newState, @EnabledFlags int flags)

1、packageName,要禁用的 APP 的包名

2、newState,就是要设置成的状态,它的取值通过注解@EnabledState进行限制,有如下几种:

/** @hide */@IntDef(prefix = { "COMPONENT_ENABLED_STATE_" }, value = {    //将APP或组件设置为manifest定义的状态。    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,})@Retention(RetentionPolicy.SOURCE)public @interface EnabledState {}

3、flags,是 Optional behavior flags,它的取值由注解@EnabledFlags限制:

/** @hide */@IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {    //仅对组件的操作起作用,用于指示禁用组件时,不 kill 组件所在的 APP。    DONT_KILL_APP})@Retention(RetentionPolicy.SOURCE)public @interface EnabledFlags {}

因此,这里我们禁用 APP 时不需要关注第三个参数,置 0 即可,代码如下:

PackageManager pm = context.getPackageManager();pm.setApplicationEnabledSetting(        pkgName,        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,        0);

如果要重新启用APP,只需按前面的限制修改第二个参数即可。

PS:
这里禁用 APP 之后,APP 图标会从 Launcher 上消失,在Settings > APP List 中可看到 APP 变为 disable 状态,用户无法使用。
如果只是想让用户无法使用,不让图标消失,可参考如下文章:
https://blog.csdn.net/qq_25815655/article/details/78355259

禁用组件

与禁用 APP,只需要 APP 包名不同,禁用组件时,还需要知道组件的名字,也就是这两个方法第一个参数不一样的原因。

setComponentEnabledSetting(ComponentName componentName,        @EnabledState int newState, @EnabledFlags int flags)

这里四大组件(activity, receiver, service, provider)都可以禁用,具体的参数说明就不再提了。

最后,禁用代码如下:

PackageManager pm = context.getPackageManager();ComponentName name = new ComponentName(pkg, clazz);pm.setComponentEnabledSetting(name,        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,        PackageManager.DONT_KILL_APP);

补充

前面说了,PackageManager 是抽象类,它通过AIDL类 IPackageManager 与 PackageManagerService 通信,上面两个方法的具体实现,也在 Service 中。

PackageManagerService 的路径如下:

frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

两个方法的实现如下:

public void setApplicationEnabledSetting(String appPackageName,    int newState, int flags, int userId, String callingPackage) {    if (!sUserManager.exists(userId)) return;    if (callingPackage == null) {        callingPackage = Integer.toString(Binder.getCallingUid());    }    setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);}@Overridepublic void setComponentEnabledSetting(ComponentName componentName,    int newState, int flags, int userId) {    if (!sUserManager.exists(userId)) return;    setEnabledSetting(componentName.getPackageName(),            componentName.getClassName(), newState, flags, userId, null);}

我们可以看到它们最后都是调用了 setEnabledSetting 方法,这个方法的实现代码很多,这里我将它贴出来就不一一分析了,有兴许可以自己研究下。

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;    int packageUid = -1;    ArrayList components;    // reader    synchronized (mPackages) {        pkgSetting = mSettings.mPackages.get(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        if (!allowedByPermission                || filterAppAccessLPr(pkgSetting, callingUid, userId)) {            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);        }    }    synchronized (mPackages) {        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 (mPackages) {            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 PackageParser.Package deletedPkg = pkgSetting.pkg;        final boolean isSystemStub = deletedPkg.isStub                && deletedPkg.isSystemApp();        if (isSystemStub                && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT                        || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {            final File codePath = decompressPackage(deletedPkg);            if (codePath == null) {                Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);                return;            }            // TODO remove direct parsing of the package object during internal cleanup            // of scan package            // We need to call parse directly here for no other reason than we need            // the new package in order to disable the old one [we use the information            // for some internal optimization to optionally create a new package setting            // object on replace]. However, we can't get the package from the scan            // because the scan modifies live structures and we need to remove the            // old [system] package from the system before a scan can be attempted.            // Once scan is indempotent we can remove this parse and use the package            // object we scanned, prior to adding it to package settings.            final PackageParser pp = new PackageParser();            pp.setSeparateProcesses(mSeparateProcesses);            pp.setDisplayMetrics(mMetrics);            pp.setCallback(mPackageParserCallback);            final PackageParser.Package tmpPkg;            try {                final int parseFlags = mDefParseFlags                        | PackageParser.PARSE_MUST_BE_APK                        | PackageParser.PARSE_IS_SYSTEM                        | PackageParser.PARSE_IS_SYSTEM_DIR;                tmpPkg = pp.parsePackage(codePath, parseFlags);            } catch (PackageParserException e) {                Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);                return;            }            synchronized (mInstallLock) {                // Disable the stub and remove any package entries                removePackageLI(deletedPkg, true);                synchronized (mPackages) {                    disableSystemPackageLPw(deletedPkg, tmpPkg);                }                final PackageParser.Package newPkg;                try (PackageFreezer freezer =                        freezePackage(deletedPkg.packageName, "setEnabledSetting")) {                    final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY                            | PackageParser.PARSE_ENFORCE_CODE;                    newPkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,                            0 /*currentTime*/, null /*user*/);                    prepareAppDataAfterInstallLIF(newPkg);                    synchronized (mPackages) {                        try {                            updateSharedLibrariesLPr(newPkg, null);                        } catch (PackageManagerException e) {                            Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);                        }                        updatePermissionsLPw(newPkg.packageName, newPkg,                                UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);                        mSettings.writeLPr();                    }                } catch (PackageManagerException e) {                    // Whoops! Something went wrong; try to roll back to the stub                    Slog.w(TAG, "Failed to install compressed system package:"                            + pkgSetting.name, e);                    // Remove the failed install                    removeCodePathLI(codePath);                    // Install the system package                    try (PackageFreezer freezer =                            freezePackage(deletedPkg.packageName, "setEnabledSetting")) {                        synchronized (mPackages) {                            // NOTE: The system package always needs to be enabled; even                            // if it's for a compressed stub. If we don't, installing the                            // system package fails during scan [scanning checks the disabled                            // packages]. We will reverse this later, after we've "installed"                            // the stub.                            // This leaves us in a fragile state; the stub should never be                            // enabled, so, cross your fingers and hope nothing goes wrong                            // until we can disable the package later.                            enableSystemPackageLPw(deletedPkg);                        }                        installPackageFromSystemLIF(new File(deletedPkg.codePath),                                false /*isPrivileged*/, null /*allUserHandles*/,                                null /*origUserHandles*/, null /*origPermissionsState*/,                                true /*writeSettings*/);                    } catch (PackageManagerException pme) {                        Slog.w(TAG, "Failed to restore system package:"                                + deletedPkg.packageName, pme);                    } finally {                        synchronized (mPackages) {                            mSettings.disableSystemPackageLPw(                                    deletedPkg.packageName, true /*replaced*/);                            mSettings.writeLPr();                        }                    }                    return;                }                clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE                        | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);                clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);                mDexManager.notifyPackageUpdated(newPkg.packageName,                        newPkg.baseCodePath, newPkg.splitCodePaths);            }        }        if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT            || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {            // Don't care about who enables an app.            callingPackage = null;        }        synchronized (mPackages) {            pkgSetting.setEnabled(newState, userId, callingPackage);        }    } else {        synchronized (mPackages) {            // We're dealing with a component level state change            // First, verify that this is a valid class name.            PackageParser.Package pkg = pkgSetting.pkg;            if (pkg == null || !pkg.hasComponentClassName(className)) {                if (pkg != null &&                        pkg.applicationInfo.targetSdkVersion >=                                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);                }            }            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 (mPackages) {        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                mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);            }        }    }    long callingId = Binder.clearCallingIdentity();    try {        if (sendNow) {            packageUid = UserHandle.getUid(userId, pkgSetting.appId);            sendPackageChangedBroadcast(packageName,                    (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);        }    } finally {        Binder.restoreCallingIdentity(callingId);    }}

更多相关文章

  1. Android中Sample的使用方法
  2. Android(安卓)常用的画图方法
  3. Android:Loader
  4. Android图片圆角 用简单的方法实现
  5. Android百度地图基础实现(标记+GPS)
  6. Android(安卓)格式化内部存储
  7. Android(安卓)格式化内部存储
  8. 【Android】安卓中常用的图片加载方法
  9. Android获取设备状态栏status bar高度的正确姿势

随机推荐

  1. Android推送通知
  2. android加载.swf flash文件
  3. Android(安卓)设置铃声
  4. Android漂亮的Help提示框
  5. android之网络请求框架OKhttp、原生http
  6. android通用JSON解析
  7. android升级数据库(Sqlite)简单示例
  8. Android(安卓)studio 开发一个用户登录界
  9. Android(安卓)AnalogClock and DigitalCl
  10. android 控件 NumberPicker 简单使用