最近开发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,