Android(安卓)禁用 APP 或四大组件
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); }}
更多相关文章
- Android中Sample的使用方法
- Android(安卓)常用的画图方法
- Android:Loader
- Android图片圆角 用简单的方法实现
- Android百度地图基础实现(标记+GPS)
- Android(安卓)格式化内部存储
- Android(安卓)格式化内部存储
- 【Android】安卓中常用的图片加载方法
- Android获取设备状态栏status bar高度的正确姿势