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

  1. /**
  2. * Set the enabled setting for an application
  3. * This setting will override any enabled state which may have been set by the application in
  4. * its manifest. It also overrides the enabled state set in the manifest for any of the
  5. * application's components. It does not override any enabled state set by
  6. * {@link #setComponentEnabledSetting} for any of the application's components.
  7. *
  8. * @param packageName The package name of the application to enable
  9. * @param newState The new enabled state for the application.
  10. * @param flags Optional behavior flags.
  11. */
  12. public abstract void setApplicationEnabledSetting(String packageName,
  13. @EnabledState int newState, @EnabledFlags int flags);
  14. /**
  15. * Set the enabled setting for a package component (activity, receiver, service, provider).
  16. * This setting will override any enabled state which may have been set by the component in its
  17. * manifest.
  18. *
  19. * @param componentName The component to enable
  20. * @param newState The new enabled state for the component.
  21. * @param flags Optional behavior flags.
  22. */
  23. public abstract void setComponentEnabledSetting(ComponentName componentName,
  24. @EnabledState int newState, @EnabledFlags int flags);

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

权限获取

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

1、在 Manifest 里面声明权限:
  1. <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 进程中:

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

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

添加好权限后,接下来就是使用了。
既然是 PackageManager 的方法,而且这两个方法也不是静态的,那就要先获取 PackageManager 的实例了。这两个方法还是 abstract 的,那就说明 PackageManager 也是 abstract 的,不能 new ,那如何获取实例呢?
我们知道系统很多 Manager 都是单实例不能 new 的,PackageManager 也一样,那是不是也是通过 getSystemService 获取呢?并不是,注释给出来了:

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

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

禁用 APP

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

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

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

  1. /** @hide */
  2. @IntDef(prefix = { "COMPONENT_ENABLED_STATE_" }, value = {
  3. //将APP或组件设置为manifest定义的状态。
  4. COMPONENT_ENABLED_STATE_DEFAULT,
  5. //启用APP或组件,忽略manifest的定义。
  6. COMPONENT_ENABLED_STATE_ENABLED,
  7. //禁用APP或组件,忽略manifest的定义。
  8. COMPONENT_ENABLED_STATE_DISABLED,
  9. //以用户身份禁用APP,忽略manifest的定义。不能用于组件操作。
  10. COMPONENT_ENABLED_STATE_DISABLED_USER,
  11. //禁用APP直到用户想用才出现。也就是说,正常情况下,用户看不到(比如在Launcher上);但是特殊情况下,用户还是能看到并选择到(比如输入法APP)。不能用于组件操作。
  12. COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
  13. })
  14. @Retention(RetentionPolicy.SOURCE)
  15. public @interface EnabledState {}

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

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

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

  1. PackageManager pm = context.getPackageManager();
  2. pm.setApplicationEnabledSetting(
  3. pkgName,
  4. PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
  5. 0
  6. );

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

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

禁用组件

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

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

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

  1. PackageManager pm = context.getPackageManager();
  2. ComponentName name = new ComponentName(pkg, clazz);
  3. pm.setComponentEnabledSetting(name,
  4. PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
  5. PackageManager.DONT_KILL_APP);

补充

前面说了,PackageManager 是抽象类,它通过AIDL类 IPackageManager 与 PackageManagerService 通信,上面两个方法的具体实现,也在 Service 中。
PackageManagerService 的路径如下:
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
两个方法的实现如下:

  1. public void setApplicationEnabledSetting(String appPackageName,
  2. int newState, int flags, int userId, String callingPackage) {
  3. if (!sUserManager.exists(userId)) return;
  4. if (callingPackage == null) {
  5. callingPackage = Integer.toString(Binder.getCallingUid());
  6. }
  7. setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
  8. }
  9. @Override
  10. public void setComponentEnabledSetting(ComponentName componentName,
  11. int newState, int flags, int userId) {
  12. if (!sUserManager.exists(userId)) return;
  13. setEnabledSetting(componentName.getPackageName(),
  14. componentName.getClassName(), newState, flags, userId, null);
  15. }

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

  1. private void setEnabledSetting(final String packageName, String className, int newState,
  2. final int flags, int userId, String callingPackage) {
  3. if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
  4. || newState == COMPONENT_ENABLED_STATE_ENABLED
  5. || newState == COMPONENT_ENABLED_STATE_DISABLED
  6. || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
  7. || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
  8. throw new IllegalArgumentException("Invalid new component state: "
  9. + newState);
  10. }
  11. PackageSetting pkgSetting;
  12. final int callingUid = Binder.getCallingUid();
  13. final int permission;
  14. if (callingUid == Process.SYSTEM_UID) {
  15. permission = PackageManager.PERMISSION_GRANTED;
  16. } else {
  17. permission = mContext.checkCallingOrSelfPermission(
  18. android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
  19. }
  20. enforceCrossUserPermission(callingUid, userId,
  21. false /* requireFullPermission */, true /* checkShell */, "set enabled");
  22. final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
  23. boolean sendNow = false;
  24. boolean isApp = (className == null);
  25. final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
  26. String componentName = isApp ? packageName : className;
  27. int packageUid = -1;
  28. ArrayList<String> components;
  29. // reader
  30. synchronized (mPackages) {
  31. pkgSetting = mSettings.mPackages.get(packageName);
  32. if (pkgSetting == null) {
  33. if (!isCallerInstantApp) {
  34. if (className == null) {
  35. throw new IllegalArgumentException("Unknown package: " + packageName);
  36. }
  37. throw new IllegalArgumentException(
  38. "Unknown component: " + packageName + "/" + className);
  39. } else {
  40. // throw SecurityException to prevent leaking package information
  41. throw new SecurityException(
  42. "Attempt to change component state; "
  43. + "pid=" + Binder.getCallingPid()
  44. + ", uid=" + callingUid
  45. + (className == null
  46. ? ", package=" + packageName
  47. : ", component=" + packageName + "/" + className));
  48. }
  49. }
  50. }
  51. // Limit who can change which apps
  52. if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
  53. // Don't allow apps that don't have permission to modify other apps
  54. if (!allowedByPermission
  55. || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
  56. throw new SecurityException(
  57. "Attempt to change component state; "
  58. + "pid=" + Binder.getCallingPid()
  59. + ", uid=" + callingUid
  60. + (className == null
  61. ? ", package=" + packageName
  62. : ", component=" + packageName + "/" + className));
  63. }
  64. // Don't allow changing protected packages.
  65. if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
  66. throw new SecurityException("Cannot disable a protected package: " + packageName);
  67. }
  68. }
  69. synchronized (mPackages) {
  70. if (callingUid == Process.SHELL_UID
  71. && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
  72. // Shell can only change whole packages between ENABLED and DISABLED_USER states
  73. // unless it is a test package.
  74. int oldState = pkgSetting.getEnabled(userId);
  75. if (className == null
  76. &&
  77. (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
  78. || oldState == COMPONENT_ENABLED_STATE_DEFAULT
  79. || oldState == COMPONENT_ENABLED_STATE_ENABLED)
  80. &&
  81. (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
  82. || newState == COMPONENT_ENABLED_STATE_DEFAULT
  83. || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
  84. // ok
  85. } else {
  86. throw new SecurityException(
  87. "Shell cannot change component state for " + packageName + "/"
  88. + className + " to " + newState);
  89. }
  90. }
  91. }
  92. if (className == null) {
  93. // We're dealing with an application/package level state change
  94. synchronized (mPackages) {
  95. if (pkgSetting.getEnabled(userId) == newState) {
  96. // Nothing to do
  97. return;
  98. }
  99. }
  100. // If we're enabling a system stub, there's a little more work to do.
  101. // Prior to enabling the package, we need to decompress the APK(s) to the
  102. // data partition and then replace the version on the system partition.
  103. final PackageParser.Package deletedPkg = pkgSetting.pkg;
  104. final boolean isSystemStub = deletedPkg.isStub
  105. && deletedPkg.isSystemApp();
  106. if (isSystemStub
  107. && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
  108. || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
  109. final File codePath = decompressPackage(deletedPkg);
  110. if (codePath == null) {
  111. Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);
  112. return;
  113. }
  114. // TODO remove direct parsing of the package object during internal cleanup
  115. // of scan package
  116. // We need to call parse directly here for no other reason than we need
  117. // the new package in order to disable the old one [we use the information
  118. // for some internal optimization to optionally create a new package setting
  119. // object on replace]. However, we can't get the package from the scan
  120. // because the scan modifies live structures and we need to remove the
  121. // old [system] package from the system before a scan can be attempted.
  122. // Once scan is indempotent we can remove this parse and use the package
  123. // object we scanned, prior to adding it to package settings.
  124. final PackageParser pp = new PackageParser();
  125. pp.setSeparateProcesses(mSeparateProcesses);
  126. pp.setDisplayMetrics(mMetrics);
  127. pp.setCallback(mPackageParserCallback);
  128. final PackageParser.Package tmpPkg;
  129. try {
  130. final int parseFlags = mDefParseFlags
  131. | PackageParser.PARSE_MUST_BE_APK
  132. | PackageParser.PARSE_IS_SYSTEM
  133. | PackageParser.PARSE_IS_SYSTEM_DIR;
  134. tmpPkg = pp.parsePackage(codePath, parseFlags);
  135. } catch (PackageParserException e) {
  136. Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);
  137. return;
  138. }
  139. synchronized (mInstallLock) {
  140. // Disable the stub and remove any package entries
  141. removePackageLI(deletedPkg, true);
  142. synchronized (mPackages) {
  143. disableSystemPackageLPw(deletedPkg, tmpPkg);
  144. }
  145. final PackageParser.Package newPkg;
  146. try (PackageFreezer freezer =
  147. freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
  148. final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
  149. | PackageParser.PARSE_ENFORCE_CODE;
  150. newPkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
  151. 0 /*currentTime*/, null /*user*/);
  152. prepareAppDataAfterInstallLIF(newPkg);
  153. synchronized (mPackages) {
  154. try {
  155. updateSharedLibrariesLPr(newPkg, null);
  156. } catch (PackageManagerException e) {
  157. Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
  158. }
  159. updatePermissionsLPw(newPkg.packageName, newPkg,
  160. UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
  161. mSettings.writeLPr();
  162. }
  163. } catch (PackageManagerException e) {
  164. // Whoops! Something went wrong; try to roll back to the stub
  165. Slog.w(TAG, "Failed to install compressed system package:"
  166. + pkgSetting.name, e);
  167. // Remove the failed install
  168. removeCodePathLI(codePath);
  169. // Install the system package
  170. try (PackageFreezer freezer =
  171. freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
  172. synchronized (mPackages) {
  173. // NOTE: The system package always needs to be enabled; even
  174. // if it's for a compressed stub. If we don't, installing the
  175. // system package fails during scan [scanning checks the disabled
  176. // packages]. We will reverse this later, after we've "installed"
  177. // the stub.
  178. // This leaves us in a fragile state; the stub should never be
  179. // enabled, so, cross your fingers and hope nothing goes wrong
  180. // until we can disable the package later.
  181. enableSystemPackageLPw(deletedPkg);
  182. }
  183. installPackageFromSystemLIF(new File(deletedPkg.codePath),
  184. false /*isPrivileged*/, null /*allUserHandles*/,
  185. null /*origUserHandles*/, null /*origPermissionsState*/,
  186. true /*writeSettings*/);
  187. } catch (PackageManagerException pme) {
  188. Slog.w(TAG, "Failed to restore system package:"
  189. + deletedPkg.packageName, pme);
  190. } finally {
  191. synchronized (mPackages) {
  192. mSettings.disableSystemPackageLPw(
  193. deletedPkg.packageName, true /*replaced*/);
  194. mSettings.writeLPr();
  195. }
  196. }
  197. return;
  198. }
  199. clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
  200. | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
  201. clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);
  202. mDexManager.notifyPackageUpdated(newPkg.packageName,
  203. newPkg.baseCodePath, newPkg.splitCodePaths);
  204. }
  205. }
  206. if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
  207. || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
  208. // Don't care about who enables an app.
  209. callingPackage = null;
  210. }
  211. synchronized (mPackages) {
  212. pkgSetting.setEnabled(newState, userId, callingPackage);
  213. }
  214. } else {
  215. synchronized (mPackages) {
  216. // We're dealing with a component level state change
  217. // First, verify that this is a valid class name.
  218. PackageParser.Package pkg = pkgSetting.pkg;
  219. if (pkg == null || !pkg.hasComponentClassName(className)) {
  220. if (pkg != null &&
  221. pkg.applicationInfo.targetSdkVersion >=
  222. Build.VERSION_CODES.JELLY_BEAN) {
  223. throw new IllegalArgumentException("Component class " + className
  224. + " does not exist in " + packageName);
  225. } else {
  226. Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
  227. + className + " does not exist in " + packageName);
  228. }
  229. }
  230. switch (newState) {
  231. case COMPONENT_ENABLED_STATE_ENABLED:
  232. if (!pkgSetting.enableComponentLPw(className, userId)) {
  233. return;
  234. }
  235. break;
  236. case COMPONENT_ENABLED_STATE_DISABLED:
  237. if (!pkgSetting.disableComponentLPw(className, userId)) {
  238. return;
  239. }
  240. break;
  241. case COMPONENT_ENABLED_STATE_DEFAULT:
  242. if (!pkgSetting.restoreComponentLPw(className, userId)) {
  243. return;
  244. }
  245. break;
  246. default:
  247. Slog.e(TAG, "Invalid new component state: " + newState);
  248. return;
  249. }
  250. }
  251. }
  252. synchronized (mPackages) {
  253. scheduleWritePackageRestrictionsLocked(userId);
  254. updateSequenceNumberLP(pkgSetting, new int[] { userId });
  255. final long callingId = Binder.clearCallingIdentity();
  256. try {
  257. updateInstantAppInstallerLocked(packageName);
  258. } finally {
  259. Binder.restoreCallingIdentity(callingId);
  260. }
  261. components = mPendingBroadcasts.get(userId, packageName);
  262. final boolean newPackage = components == null;
  263. if (newPackage) {
  264. components = new ArrayList<String>();
  265. }
  266. if (!components.contains(componentName)) {
  267. components.add(componentName);
  268. }
  269. if ((flags&PackageManager.DONT_KILL_APP) == 0) {
  270. sendNow = true;
  271. // Purge entry from pending broadcast list if another one exists already
  272. // since we are sending one right away.
  273. mPendingBroadcasts.remove(userId, packageName);
  274. } else {
  275. if (newPackage) {
  276. mPendingBroadcasts.put(userId, packageName, components);
  277. }
  278. if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
  279. // Schedule a message
  280. mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
  281. }
  282. }
  283. }
  284. long callingId = Binder.clearCallingIdentity();
  285. try {
  286. if (sendNow) {
  287. packageUid = UserHandle.getUid(userId, pkgSetting.appId);
  288. sendPackageChangedBroadcast(packageName,
  289. (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
  290. }
  291. } finally {
  292. Binder.restoreCallingIdentity(callingId);
  293. }
  294. }