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:
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -18,3 +18,4 @@
|
||||
package android.content.pm;
|
||||
|
||||
parcelable PackageManager.Property;
|
||||
parcelable PackageManager.ComponentEnabledSetting;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user