Add multiple components support for component enabled setting updates

This cl creates a new API setComponentsEnabledSetting in PackageManager.
It accepts a list of component changes that applies them all atomically
in order to improve performance and minimize the risk of errors for the
applications.

Bug: 189883959
Test: atest PackageManagerTest
Change-Id: I64d9f46198e26e001e79ca9c198c26929adf8596
This commit is contained in:
Rhed Jao
2021-07-19 18:35:59 +08:00
parent 5c18fc3e1a
commit db587c6e5d
6 changed files with 572 additions and 183 deletions

View File

@@ -12633,6 +12633,7 @@ package android.content.pm {
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setComponentEnabledSettings(@NonNull java.util.List<android.content.pm.PackageManager.ComponentEnabledSetting>);
method public abstract void setInstallerPackageName(@NonNull String, @Nullable String);
method public void setMimeGroup(@NonNull String, @NonNull java.util.Set<java.lang.String>);
method public abstract void updateInstantAppCookie(@Nullable byte[]);
@@ -12829,6 +12830,16 @@ package android.content.pm {
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
}
public static final class PackageManager.ComponentEnabledSetting implements android.os.Parcelable {
ctor public PackageManager.ComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
method public int describeContents();
method @Nullable public android.content.ComponentName getComponentName();
method public int getEnabledFlags();
method public int getEnabledState();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.ComponentEnabledSetting> CREATOR;
}
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
ctor public PackageManager.NameNotFoundException();
ctor public PackageManager.NameNotFoundException(String);

View File

@@ -2806,6 +2806,15 @@ public class ApplicationPackageManager extends PackageManager {
}
}
@Override
public void setComponentEnabledSettings(List<ComponentEnabledSetting> settings) {
try {
mPM.setComponentEnabledSettings(settings, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
try {

View File

@@ -40,6 +40,7 @@ import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -358,6 +359,11 @@ interface IPackageManager {
void setComponentEnabledSetting(in ComponentName componentName,
in int newState, in int flags, int userId);
/**
* As per {@link android.content.pm.PackageManager#setComponentEnabledSettings}.
*/
void setComponentEnabledSettings(in List<ComponentEnabledSetting> settings, int userId);
/**
* As per {@link android.content.pm.PackageManager#getComponentEnabledSetting}.
*/

View File

@@ -18,3 +18,4 @@
package android.content.pm;
parcelable PackageManager.Property;
parcelable PackageManager.ComponentEnabledSetting;

View File

@@ -83,6 +83,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DataClass;
import dalvik.system.VMRuntime;
@@ -379,6 +380,219 @@ public abstract class PackageManager {
};
}
/**
* The class containing the enabled setting of a package component.
* <p>
* This is used by the {@link #setComponentEnabledSettings(List)} to support the batch updates
* of the enabled settings of components.
*
* @see #setComponentEnabledSettings(List)
*/
@DataClass(genConstructor = false)
public static final class ComponentEnabledSetting implements Parcelable {
/**
* The package name of the application to enable the setting.
*/
private final @Nullable String mPackageName;
/**
* The component name of the application to enable the setting.
*/
private final @Nullable ComponentName mComponentName;
/**
* The new enabled state
*/
private final @EnabledState int mEnabledState;
/**
* The optional behavior flag
*/
private final @EnabledFlags int mEnabledFlags;
/**
* Create an instance of the ComponentEnabledSetting for the component level's enabled
* setting update.
*
* @param componentName The component name to update the enabled setting.
* @param newState The new enabled state.
* @param flags The optional behavior flags.
*/
public ComponentEnabledSetting(@NonNull ComponentName componentName,
@EnabledState int newState, @EnabledFlags int flags) {
Objects.nonNull(componentName);
mPackageName = null;
mComponentName = componentName;
mEnabledState = newState;
mEnabledFlags = flags;
}
/**
* Create an instance of the ComponentEnabledSetting for the application level's enabled
* setting update.
*
* @param packageName The package name to update the enabled setting.
* @param newState The new enabled state.
* @param flags The optional behavior flags.
* @hide
*/
public ComponentEnabledSetting(@NonNull String packageName,
@EnabledState int newState, @EnabledFlags int flags) {
Objects.nonNull(packageName);
mPackageName = packageName;
mComponentName = null;
mEnabledState = newState;
mEnabledFlags = flags;
}
/**
* Returns the package name of the setting.
*
* @return the package name.
* @hide
*/
public @NonNull String getPackageName() {
if (isComponent()) {
return mComponentName.getPackageName();
}
return mPackageName;
}
/**
* Returns the component class name of the setting.
*
* @return the class name.
* @hide
*/
public @Nullable String getClassName() {
if (isComponent()) {
return mComponentName.getClassName();
}
return null;
}
/**
* Whether or not this is for the component level's enabled setting update.
*
* @return {@code true} if it's the component level enabled setting update.
* @hide
*/
public boolean isComponent() {
return mComponentName != null;
}
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/PackageManager.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/**
* The component name of the application to enable the setting.
*/
@DataClass.Generated.Member
public @Nullable ComponentName getComponentName() {
return mComponentName;
}
/**
* The new enabled state
*/
@DataClass.Generated.Member
public @EnabledState int getEnabledState() {
return mEnabledState;
}
/**
* The optional behavior flag
*/
@DataClass.Generated.Member
public @EnabledFlags int getEnabledFlags() {
return mEnabledFlags;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
if (mPackageName != null) flg |= 0x1;
if (mComponentName != null) flg |= 0x2;
dest.writeByte(flg);
if (mPackageName != null) dest.writeString(mPackageName);
if (mComponentName != null) dest.writeTypedObject(mComponentName, flags);
dest.writeInt(mEnabledState);
dest.writeInt(mEnabledFlags);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ ComponentEnabledSetting(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
String packageName = (flg & 0x1) == 0 ? null : in.readString();
ComponentName componentName = (flg & 0x2) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
int enabledState = in.readInt();
int enabledFlags = in.readInt();
this.mPackageName = packageName;
this.mComponentName = componentName;
this.mEnabledState = enabledState;
com.android.internal.util.AnnotationValidations.validate(
EnabledState.class, null, mEnabledState);
this.mEnabledFlags = enabledFlags;
com.android.internal.util.AnnotationValidations.validate(
EnabledFlags.class, null, mEnabledFlags);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<ComponentEnabledSetting> CREATOR
= new Parcelable.Creator<ComponentEnabledSetting>() {
@Override
public ComponentEnabledSetting[] newArray(int size) {
return new ComponentEnabledSetting[size];
}
@Override
public ComponentEnabledSetting createFromParcel(@NonNull Parcel in) {
return new ComponentEnabledSetting(in);
}
};
@DataClass.Generated(
time = 1628668290863L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/PackageManager.java",
inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable android.content.ComponentName mComponentName\nprivate final @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate final @android.content.pm.PackageManager.EnabledFlags int mEnabledFlags\npublic @android.annotation.NonNull java.lang.String getPackageName()\npublic @android.annotation.Nullable java.lang.String getClassName()\npublic boolean isComponent()\nclass ComponentEnabledSetting extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}
/**
* Listener for changes in permissions granted to a UID.
*
@@ -7675,6 +7889,9 @@ public abstract class PackageManager {
* This setting will override any enabled state which may have been set by the component in its
* manifest.
*
* <p>Consider using {@link #setComponentEnabledSettings(List)} if multiple components need to
* be updated atomically.
*
* @param componentName The component to enable
* @param newState The new enabled state for the component.
* @param flags Optional behavior flags.
@@ -7684,6 +7901,32 @@ public abstract class PackageManager {
public abstract void setComponentEnabledSetting(@NonNull ComponentName componentName,
@EnabledState int newState, @EnabledFlags int flags);
/**
* Set the enabled settings for package components such as activities, receivers, services and
* providers. This setting will override any enabled state which may have been set by the
* component in its manifest.
*
* <p>This api accepts a list of component changes, and applies them all atomically. The
* application can use this api if components have dependencies and need to be updated
* atomically.
*
* <p>The permission is not required if target components are running under the same uid with
* the caller.
*
* @param settings The list of component enabled settings to update. Note that an
* {@link IllegalArgumentException} is thrown if the duplicated component name
* is in the list or there's a conflict {@link #DONT_KILL_APP} flag between
* different components in the same package.
*
* @see #setComponentEnabledSetting(ComponentName, int, int)
*/
@RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
conditional = true)
public void setComponentEnabledSettings(@NonNull List<ComponentEnabledSetting> settings) {
throw new UnsupportedOperationException("setComponentEnabledSettings not implemented"
+ "in subclass");
}
/**
* Return the enabled setting for a package component (activity,
* receiver, service, provider). This returns the last value set by

View File

@@ -177,6 +177,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.PackageManager.ComponentType;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
@@ -19900,7 +19901,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (callingPackage == null) {
callingPackage = Integer.toString(Binder.getCallingUid());
}
setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
setEnabledSettings(List.of(new ComponentEnabledSetting(appPackageName, newState, flags)),
userId, callingPackage);
}
@Override
@@ -19997,216 +20000,270 @@ public class PackageManagerService extends IPackageManager.Stub
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);
setEnabledSettings(List.of(new ComponentEnabledSetting(componentName, newState, flags)),
userId, null /* callingPackage */);
}
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);
@Override
public void setComponentEnabledSettings(List<ComponentEnabledSetting> settings, int userId) {
if (!mUserManager.exists(userId)) return;
if (settings == null || settings.isEmpty()) {
throw new IllegalArgumentException("The list of enabled settings is empty");
}
setEnabledSettings(settings, userId, null /* callingPackage */);
}
private void setEnabledSettings(List<ComponentEnabledSetting> settings, int userId,
String callingPackage) {
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);
String componentName = isApp ? packageName : className;
ArrayList<String> components;
final boolean isCallerTargetApp = ArrayUtils.contains(
getPackagesForUid(callingUid), packageName);
final PackageSetting pkgSetting;
final int targetSize = settings.size();
for (int i = 0; i < targetSize; i++) {
final int newState = settings.get(i).getEnabledState();
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);
}
}
if (targetSize > 1) {
final ArraySet<String> checkDuplicatedPackage = new ArraySet<>();
final ArraySet<ComponentName> checkDuplicatedComponent = new ArraySet<>();
final ArrayMap<String, Integer> checkConflictFlag = new ArrayMap<>();
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
final String packageName = setting.getPackageName();
if (setting.isComponent()) {
final ComponentName componentName = setting.getComponentName();
if (checkDuplicatedComponent.contains(componentName)) {
throw new IllegalArgumentException("The component " + componentName
+ " is duplicated");
}
checkDuplicatedComponent.add(componentName);
// check if there is a conflict of the DONT_KILL_APP flag between components
// in the package
final Integer enabledFlags = checkConflictFlag.get(packageName);
if (enabledFlags == null) {
checkConflictFlag.put(packageName, setting.getEnabledFlags());
} else if ((enabledFlags & PackageManager.DONT_KILL_APP)
!= (setting.getEnabledFlags() & PackageManager.DONT_KILL_APP)) {
throw new IllegalArgumentException("A conflict of the DONT_KILL_APP flag "
+ "between components in the package " + packageName);
}
} else {
if (checkDuplicatedPackage.contains(packageName)) {
throw new IllegalArgumentException("The package " + packageName
+ " is duplicated");
}
checkDuplicatedPackage.add(packageName);
}
}
}
final boolean allowedByPermission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE) == PERMISSION_GRANTED;
final boolean[] updateAllowed = new boolean[targetSize];
Arrays.fill(updateAllowed, true);
final Map<String, PackageSetting> pkgSettings = new ArrayMap<>(targetSize);
// reader
synchronized (mLock) {
pkgSetting = mSettings.getPackageLPr(packageName);
// Limit who can change which apps
if (!isCallerTargetApp) {
// Don't allow apps that don't have permission to modify other apps
// Checks for target packages
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
final String packageName = setting.getPackageName();
if (pkgSettings.containsKey(packageName)) {
// this package has verified
continue;
}
final boolean isCallerTargetApp = ArrayUtils.contains(
getPackagesForUid(callingUid), packageName);
final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
// Limit who can change which apps
if (!isCallerTargetApp) {
// Don't allow apps that don't have permission to modify other apps
if (!allowedByPermission
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
throw new SecurityException("Attempt to change component state; "
+ "pid=" + Binder.getCallingPid()
+ ", uid=" + callingUid
+ (!setting.isComponent() ? ", package=" + packageName
: ", component=" + setting.getComponentName()));
}
// Don't allow changing protected packages.
if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
throw new SecurityException(
"Cannot disable a protected package: " + packageName);
}
}
if (pkgSetting == null) {
throw new IllegalArgumentException(setting.isComponent()
? "Unknown component: " + setting.getComponentName()
: "Unknown package: " + packageName);
}
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.
final int oldState = pkgSetting.getEnabled(userId);
final int newState = setting.getEnabledState();
if (!setting.isComponent()
&&
(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 "
+ setting.getComponentName() + " to " + newState);
}
}
pkgSettings.put(packageName, pkgSetting);
}
// Checks for target components
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
// skip if it's application
if (!setting.isComponent()) continue;
// Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
// app details activity
final String packageName = setting.getPackageName();
final String className = setting.getClassName();
if (!allowedByPermission
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
throw new SecurityException(
"Attempt to change component state; "
+ "pid=" + Binder.getCallingPid()
+ ", uid=" + callingUid
+ (className == null
? ", package=" + packageName
: ", component=" + packageName + "/" + className));
&& PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
throw new SecurityException("Cannot disable a system-generated component");
}
// Don't allow changing protected packages.
if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
throw new SecurityException(
"Cannot disable a protected package: " + packageName);
}
}
if (pkgSetting == null) {
if (className == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
throw new IllegalArgumentException(
"Unknown component: " + packageName + "/" + className);
}
}
// 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;
// Verify that this is a valid class name.
final AndroidPackage pkg = pkgSettings.get(packageName).getPkg();
if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
if (pkg != null &&
pkg.getTargetSdkVersion() >=
Build.VERSION_CODES.JELLY_BEAN) {
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);
updateAllowed[i] = false;
}
}
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;
}
}
}
// More work for application enabled setting updates
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
// skip if it's component
if (setting.isComponent()) continue;
final PackageSetting pkgSetting = pkgSettings.get(setting.getPackageName());
final int newState = setting.getEnabledState();
synchronized (mLock) {
if (pkgSetting.getEnabled(userId) == newState) {
// Nothing to do
updateAllowed[i] = false;
continue;
}
}
// 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.getPkg();
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)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
continue;
}
}
}
// packageName -> list of components to send broadcasts now
final ArrayMap<String, ArrayList<String>> sendNowBroadcasts = new ArrayMap<>(targetSize);
synchronized (mLock) {
if ((flags & PackageManager.SYNCHRONOUS) != 0) {
boolean scheduleBroadcastMessage = false;
boolean isSynchronous = false;
boolean anyChanged = false;
for (int i = 0; i < targetSize; i++) {
if (!updateAllowed[i]) {
continue;
}
// update enabled settings
final ComponentEnabledSetting setting = settings.get(i);
final String packageName = setting.getPackageName();
if (!setEnabledSettingInternalLocked(pkgSettings.get(packageName), setting,
userId, callingPackage)) {
continue;
}
anyChanged = true;
if ((setting.getEnabledFlags() & PackageManager.SYNCHRONOUS) != 0) {
isSynchronous = true;
}
// collect broadcast list for the package
final String componentName = setting.isComponent()
? setting.getClassName() : packageName;
ArrayList<String> componentList = sendNowBroadcasts.get(packageName);
if (componentList == null) {
componentList = mPendingBroadcasts.get(userId, packageName);
}
final boolean newPackage = componentList == null;
if (newPackage) {
componentList = new ArrayList<>();
}
if (!componentList.contains(componentName)) {
componentList.add(componentName);
}
if ((setting.getEnabledFlags() & PackageManager.DONT_KILL_APP) == 0) {
sendNowBroadcasts.put(packageName, componentList);
// 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, componentList);
}
scheduleBroadcastMessage = true;
}
}
if (!anyChanged) {
// nothing changed, return immediately
return;
}
if (isSynchronous) {
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 (scheduleBroadcastMessage) {
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;
? BROADCAST_DELAY
: BROADCAST_DELAY_DURING_STARTUP;
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay);
}
}
@@ -20214,16 +20271,78 @@ public class PackageManagerService extends IPackageManager.Stub
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);
for (int i = 0; i < sendNowBroadcasts.size(); i++) {
final String packageName = sendNowBroadcasts.keyAt(i);
final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
final int packageUid = UserHandle.getUid(
userId, pkgSettings.get(packageName).appId);
sendPackageChangedBroadcast(packageName, false /* dontKillApp */,
components, packageUid, null /* reason */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
private boolean setEnabledSettingInternalLocked(PackageSetting pkgSetting,
ComponentEnabledSetting setting, int userId, String callingPackage) {
final int newState = setting.getEnabledState();
final String packageName = setting.getPackageName();
boolean success = false;
if (!setting.isComponent()) {
// We're dealing with an application/package level state change
if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
// Don't care about who enables an app.
callingPackage = null;
}
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);
}
success = true;
} else {
// We're dealing with a component level state change
final String className = setting.getClassName();
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
success = pkgSetting.enableComponentLPw(className, userId);
break;
case COMPONENT_ENABLED_STATE_DISABLED:
success = pkgSetting.disableComponentLPw(className, userId);
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
success = pkgSetting.restoreComponentLPw(className, userId);
break;
default:
Slog.e(TAG, "Failed setComponentEnabledSetting: component "
+ packageName + "/" + className
+ " requested an invalid new component state: " + newState);
break;
}
}
if (!success) {
return false;
}
updateSequenceNumberLP(pkgSetting, new int[] { userId });
final long callingId = Binder.clearCallingIdentity();
try {
updateInstantAppInstallerLocked(packageName);
} finally {
Binder.restoreCallingIdentity(callingId);
}
return true;
}
@WorkerThread
@Override
public void flushPackageRestrictionsAsUser(int userId) {