diff options
19 files changed, 629 insertions, 277 deletions
diff --git a/api/current.txt b/api/current.txt index eeaabf661e73..f56400c9a4db 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11392,7 +11392,7 @@ package android.content.pm { method public abstract void onPackageRemoved(String, android.os.UserHandle); method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean); method public void onPackagesSuspended(String[], android.os.UserHandle); - method public void onPackagesSuspended(String[], android.os.UserHandle, @Nullable android.os.Bundle); + method @Deprecated public void onPackagesSuspended(String[], android.os.UserHandle, @Nullable android.os.Bundle); method public abstract void onPackagesUnavailable(String[], android.os.UserHandle, boolean); method public void onPackagesUnsuspended(String[], android.os.UserHandle); method public void onShortcutsChanged(@NonNull String, @NonNull java.util.List<android.content.pm.ShortcutInfo>, @NonNull android.os.UserHandle); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index d74399c54bda..a2013075b379 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2414,14 +2414,11 @@ public class ApplicationPackageManager extends PackageManager { @Override public Bundle getSuspendedPackageAppExtras() { - final PersistableBundle extras; try { - extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(), - getUserId()); + return mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(), getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return extras != null ? new Bundle(extras.deepCopy()) : null; } @Override diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3418b7be42d6..40d9a0dfb095 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2025,17 +2025,6 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; /** - * Intent extra: A {@link Bundle} of extras supplied for the launcher when any packages on - * device are suspended. Will be sent with {@link #ACTION_PACKAGES_SUSPENDED}. - * - * @see PackageManager#isPackageSuspended() - * @see #ACTION_PACKAGES_SUSPENDED - * - * @hide - */ - public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS"; - - /** * Intent extra: ID of the shortcut used to send the share intent. Will be sent with * {@link #ACTION_SEND}. * diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c6beee2f898a..4d7c43ace923 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -50,6 +50,7 @@ import android.content.pm.VersionedPackage; import android.content.pm.dex.IArtManager; import android.graphics.Bitmap; import android.net.Uri; +import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.content.IntentSender; @@ -280,7 +281,7 @@ interface IPackageManager { boolean isPackageSuspendedForUser(String packageName, int userId); - PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId); + Bundle getSuspendedPackageAppExtras(String packageName, int userId); /** * Backup/restore support - only the system uid may use these. diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index c74daa8eadfc..5650b25d73f0 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -248,7 +248,11 @@ public class LauncherApps { * system, {@code null} otherwise. * @see PackageManager#isPackageSuspended() * @see #getSuspendedPackageLauncherExtras(String, UserHandle) + * @deprecated {@code launcherExtras} should be obtained by using + * {@link #getSuspendedPackageLauncherExtras(String, UserHandle)}. For all other cases, + * {@link #onPackagesSuspended(String[], UserHandle)} should be used. */ + @Deprecated public void onPackagesSuspended(String[] packageNames, UserHandle user, @Nullable Bundle launcherExtras) { onPackagesSuspended(packageNames, user); diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 24ee21360ed8..0a50125a7bb1 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -191,11 +191,13 @@ public abstract class PackageManagerInternal { * suspended application. * * @param suspendedPackage The package that has been suspended. + * @param suspendingPackage * @param userId The user for which to check. * @return A {@link SuspendDialogInfo} object describing the dialog to be shown. */ @Nullable - public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId); + public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, + String suspendingPackage, int userId); /** * Gets any distraction flags set via diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 249b6919fc28..5c74efb8ff1b 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -31,6 +31,7 @@ import android.annotation.UnsupportedAppUsage; import android.os.BaseBundle; import android.os.Debug; import android.os.PersistableBundle; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Slog; @@ -38,6 +39,11 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; import java.util.Arrays; import java.util.Objects; @@ -56,10 +62,7 @@ public class PackageUserState { public boolean hidden; // Is the app restricted by owner / admin public int distractionFlags; public boolean suspended; - public String suspendingPackage; - public SuspendDialogInfo dialogInfo; - public PersistableBundle suspendedAppExtras; - public PersistableBundle suspendedLauncherExtras; + public ArrayMap<String, SuspendParams> suspendParams; // Suspending package to suspend params public boolean instantApp; public boolean virtualPreload; public int enabled; @@ -95,10 +98,7 @@ public class PackageUserState { hidden = o.hidden; distractionFlags = o.distractionFlags; suspended = o.suspended; - suspendingPackage = o.suspendingPackage; - dialogInfo = o.dialogInfo; - suspendedAppExtras = o.suspendedAppExtras; - suspendedLauncherExtras = o.suspendedLauncherExtras; + suspendParams = new ArrayMap<>(o.suspendParams); instantApp = o.instantApp; virtualPreload = o.virtualPreload; enabled = o.enabled; @@ -231,19 +231,7 @@ public class PackageUserState { return false; } if (suspended) { - if (suspendingPackage == null - || !suspendingPackage.equals(oldState.suspendingPackage)) { - return false; - } - if (!Objects.equals(dialogInfo, oldState.dialogInfo)) { - return false; - } - if (!BaseBundle.kindofEquals(suspendedAppExtras, - oldState.suspendedAppExtras)) { - return false; - } - if (!BaseBundle.kindofEquals(suspendedLauncherExtras, - oldState.suspendedLauncherExtras)) { + if (!Objects.equals(suspendParams, oldState.suspendParams)) { return false; } } @@ -308,4 +296,171 @@ public class PackageUserState { } return true; } + + @Override + public int hashCode() { + int hashCode = Long.hashCode(ceDataInode); + hashCode = 31 * hashCode + Boolean.hashCode(installed); + hashCode = 31 * hashCode + Boolean.hashCode(stopped); + hashCode = 31 * hashCode + Boolean.hashCode(notLaunched); + hashCode = 31 * hashCode + Boolean.hashCode(hidden); + hashCode = 31 * hashCode + distractionFlags; + hashCode = 31 * hashCode + Boolean.hashCode(suspended); + hashCode = 31 * hashCode + Objects.hashCode(suspendParams); + hashCode = 31 * hashCode + Boolean.hashCode(instantApp); + hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload); + hashCode = 31 * hashCode + enabled; + hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller); + hashCode = 31 * hashCode + domainVerificationStatus; + hashCode = 31 * hashCode + appLinkGeneration; + hashCode = 31 * hashCode + categoryHint; + hashCode = 31 * hashCode + installReason; + hashCode = 31 * hashCode + Objects.hashCode(disabledComponents); + hashCode = 31 * hashCode + Objects.hashCode(enabledComponents); + hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning); + return hashCode; + } + + /** + * Container to describe suspension parameters. + */ + public static final class SuspendParams { + private static final String TAG_DIALOG_INFO = "dialog-info"; + private static final String TAG_APP_EXTRAS = "app-extras"; + private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras"; + + public SuspendDialogInfo dialogInfo; + public PersistableBundle appExtras; + public PersistableBundle launcherExtras; + + private SuspendParams() { + } + + /** + * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all + * the fields are {@code null}. + * + * @param dialogInfo + * @param appExtras + * @param launcherExtras + * @return A {@link SuspendParams} object or {@code null}. + */ + public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo, + PersistableBundle appExtras, PersistableBundle launcherExtras) { + if (dialogInfo == null && appExtras == null && launcherExtras == null) { + return null; + } + final SuspendParams instance = new SuspendParams(); + instance.dialogInfo = dialogInfo; + instance.appExtras = appExtras; + instance.launcherExtras = launcherExtras; + return instance; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SuspendParams)) { + return false; + } + final SuspendParams other = (SuspendParams) obj; + if (!Objects.equals(dialogInfo, other.dialogInfo)) { + return false; + } + if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) { + return false; + } + if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hashCode = Objects.hashCode(dialogInfo); + hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0); + hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0); + return hashCode; + } + + /** + * Serializes this object into an xml format + * @param out the {@link XmlSerializer} object + * @throws IOException + */ + public void saveToXml(XmlSerializer out) throws IOException { + if (dialogInfo != null) { + out.startTag(null, TAG_DIALOG_INFO); + dialogInfo.saveToXml(out); + out.endTag(null, TAG_DIALOG_INFO); + } + if (appExtras != null) { + out.startTag(null, TAG_APP_EXTRAS); + try { + appExtras.saveToXml(out); + } catch (XmlPullParserException e) { + Slog.e(LOG_TAG, "Exception while trying to write appExtras." + + " Will be lost on reboot", e); + } + out.endTag(null, TAG_APP_EXTRAS); + } + if (launcherExtras != null) { + out.startTag(null, TAG_LAUNCHER_EXTRAS); + try { + launcherExtras.saveToXml(out); + } catch (XmlPullParserException e) { + Slog.e(LOG_TAG, "Exception while trying to write launcherExtras." + + " Will be lost on reboot", e); + } + out.endTag(null, TAG_LAUNCHER_EXTRAS); + } + } + + /** + * Parses this object from the xml format. Returns {@code null} if no object related + * information could be read. + * @param in the reader + * @return + */ + public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException { + SuspendDialogInfo readDialogInfo = null; + PersistableBundle readAppExtras = null; + PersistableBundle readLauncherExtras = null; + + final int currentDepth = in.getDepth(); + int type; + try { + while ((type = in.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || in.getDepth() > currentDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + switch (in.getName()) { + case TAG_DIALOG_INFO: + readDialogInfo = SuspendDialogInfo.restoreFromXml(in); + break; + case TAG_APP_EXTRAS: + readAppExtras = PersistableBundle.restoreFromXml(in); + break; + case TAG_LAUNCHER_EXTRAS: + readLauncherExtras = PersistableBundle.restoreFromXml(in); + break; + default: + Slog.w(LOG_TAG, "Unknown tag " + in.getName() + + " in SuspendParams. Ignoring"); + break; + } + } + } catch (XmlPullParserException e) { + Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams," + + " some fields may default", e); + } + return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras); + } + } } diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index c928f3f39f03..d6dcb29e0682 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -22,11 +22,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.util.Slog; + import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; @@ -203,10 +203,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onPackagesSuspended(String[] packages) { } - public void onPackagesSuspended(String[] packages, Bundle launcherExtras) { - onPackagesSuspended(packages); - } - public void onPackagesUnsuspended(String[] packages) { } @@ -446,9 +442,8 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } } else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - final Bundle launcherExtras = intent.getBundleExtra(Intent.EXTRA_LAUNCHER_EXTRAS); mSomePackagesChanged = true; - onPackagesSuspended(pkgList, launcherExtras); + onPackagesSuspended(pkgList); } else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) { String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); mSomePackagesChanged = true; diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index 6ffa0c943037..301fa13ce4d8 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -110,7 +110,7 @@ message PackageProto { optional bool is_launched = 6; optional EnabledState enabled_state = 7; optional string last_disabled_app_caller = 8; - optional string suspending_package = 9; + repeated string suspending_package = 9; optional int32 distraction_flags = 10; } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index fea4e9047f83..81ce359cc078 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -631,8 +631,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent( providerUserId, true); } else { - final SuspendDialogInfo dialogInfo = mPackageManagerInternal - .getSuspendedDialogInfo(providerPackage, providerUserId); + final SuspendDialogInfo dialogInfo = + mPackageManagerInternal.getSuspendedDialogInfo(providerPackage, + suspendingPackage, providerUserId); onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent( providerPackage, suspendingPackage, dialogInfo, providerUserId); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 3464cab99d93..2b6c347fe726 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -62,6 +62,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.provider.Settings; import android.util.Log; +import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -1024,8 +1025,22 @@ public class LauncherAppsService extends SystemService { } @Override - public void onPackagesSuspended(String[] packages, Bundle launcherExtras) { + public void onPackagesSuspended(String[] packages) { UserHandle user = new UserHandle(getChangingUserId()); + PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + final ArrayList<Pair<String, Bundle>> packagesWithExtras = new ArrayList<>(); + final ArrayList<String> packagesWithoutExtras = new ArrayList<>(); + for (String pkg : packages) { + final Bundle launcherExtras = pmi.getSuspendedPackageLauncherExtras(pkg, + user.getIdentifier()); + if (launcherExtras != null) { + packagesWithExtras.add(new Pair<>(pkg, launcherExtras)); + } else { + packagesWithoutExtras.add(pkg); + } + } + final String[] packagesNullExtras = packagesWithoutExtras.toArray( + new String[packagesWithoutExtras.size()]); final int n = mListeners.beginBroadcast(); try { for (int i = 0; i < n; i++) { @@ -1033,7 +1048,13 @@ public class LauncherAppsService extends SystemService { BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i); if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) continue; try { - listener.onPackagesSuspended(user, packages, launcherExtras); + listener.onPackagesSuspended(user, packagesNullExtras, null); + for (int idx = 0; idx < packagesWithExtras.size(); idx++) { + Pair<String, Bundle> packageExtraPair = packagesWithExtras.get(idx); + listener.onPackagesSuspended(user, + new String[]{packageExtraPair.first}, + packageExtraPair.second); + } } catch (RemoteException re) { Slog.d(TAG, "Callback failed ", re); } @@ -1041,8 +1062,6 @@ public class LauncherAppsService extends SystemService { } finally { mListeners.finishBroadcast(); } - - super.onPackagesSuspended(packages, launcherExtras); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index fe3d2c8fa981..3c6016f8afc3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -372,7 +372,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Predicate; /** * Keep track of all those APKs everywhere. @@ -12665,14 +12664,10 @@ public class PackageManagerService extends IPackageManager.Stub } private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId, - boolean suspended, PersistableBundle launcherExtras) { + boolean suspended) { final Bundle extras = new Bundle(3); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - if (launcherExtras != null) { - extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS, - new Bundle(launcherExtras.deepCopy())); - } sendPackageBroadcast( suspended ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED, @@ -12937,8 +12932,6 @@ public class PackageManagerService extends IPackageManager.Stub if (ownerUid == callingUid) { return; } - throw new UnsupportedOperationException("Cannot suspend/unsuspend packages. User " - + userId + " has an active DO or PO"); } mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, @@ -12946,6 +12939,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageUid = getPackageUid(callingPackage, 0, userId); final boolean allowedPackageUid = packageUid == callingUid; + // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL final boolean allowedShell = callingUid == SHELL_UID && UserHandle.isSameApp(packageUid, callingUid); @@ -12996,20 +12990,27 @@ public class PackageManagerService extends IPackageManager.Stub unactionedPackages.add(packageName); continue; } + boolean packageUnsuspended; synchronized (mLock) { - pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, - launcherExtras, userId); + if (suspended) { + pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras, + launcherExtras, userId); + } else { + pkgSetting.removeSuspension(callingPackage, userId); + } + packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId); + } + if (suspended || packageUnsuspended) { + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } - changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } if (!changedPackagesList.isEmpty()) { final String[] changedPackages = changedPackagesList.toArray( new String[changedPackagesList.size()]); - sendPackagesSuspendedForUser( - changedPackages, changedUids.toArray(), userId, suspended, launcherExtras); - sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId); + sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended); + sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId); synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); } @@ -13018,38 +13019,40 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) { + public Bundle getSuspendedPackageAppExtras(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); if (getPackageUid(packageName, 0, userId) != callingUid) { throw new SecurityException("Calling package " + packageName + " does not belong to calling uid " + callingUid); } + return getSuspendedPackageAppExtrasInternal(packageName, userId); + } + + private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) { synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { + if (ps == null) { throw new IllegalArgumentException("Unknown target package: " + packageName); } - final PackageUserState packageUserState = ps.readUserState(userId); - if (packageUserState.suspended) { - return packageUserState.suspendedAppExtras; + final PackageUserState pus = ps.readUserState(userId); + final Bundle allExtras = new Bundle(); + if (pus.suspended) { + for (int i = 0; i < pus.suspendParams.size(); i++) { + final PackageUserState.SuspendParams params = pus.suspendParams.valueAt(i); + if (params != null && params.appExtras != null) { + allExtras.putAll(params.appExtras); + } + } } - return null; + return (allExtras.size() > 0) ? allExtras : null; } } private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended, - PersistableBundle appExtras, int userId) { - final String action; - final Bundle intentExtras = new Bundle(); - if (suspended) { - action = Intent.ACTION_MY_PACKAGE_SUSPENDED; - if (appExtras != null) { - final Bundle bundledAppExtras = new Bundle(appExtras.deepCopy()); - intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, bundledAppExtras); - } - } else { - action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED; - } + int userId) { + final String action = suspended + ? Intent.ACTION_MY_PACKAGE_SUSPENDED + : Intent.ACTION_MY_PACKAGE_UNSUSPENDED; mHandler.post(() -> { try { final IActivityManager am = ActivityManager.getService(); @@ -13060,6 +13063,16 @@ public class PackageManagerService extends IPackageManager.Stub } final int[] targetUserIds = new int[] {userId}; for (String packageName : affectedPackages) { + final Bundle appExtras = suspended + ? getSuspendedPackageAppExtrasInternal(packageName, userId) + : null; + final Bundle intentExtras; + if (appExtras != null) { + intentExtras = new Bundle(1); + intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras); + } else { + intentExtras = null; + } doSendBroadcast(am, action, null, intentExtras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null, targetUserIds, false); @@ -13092,57 +13105,32 @@ public class PackageManagerService extends IPackageManager.Stub * <p><b>Should not be used on a frequent code path</b> as it flushes state to disk * synchronously * - * @param packageName The package holding {@link Manifest.permission#SUSPEND_APPS} permission - * @param affectedUser The user for which the changes are taking place. - */ - void unsuspendForSuspendingPackage(final String packageName, int affectedUser) { - final int[] userIds = (affectedUser == UserHandle.USER_ALL) ? mUserManager.getUserIds() - : new int[] {affectedUser}; - for (int userId : userIds) { - unsuspendForSuspendingPackages(packageName::equals, userId); - } - } - - /** - * Immediately unsuspends any packages in the given users not suspended by the platform or root. - * To be called when a profile owner or a device owner is added. - * - * <p><b>Should not be used on a frequent code path</b> as it flushes state to disk - * synchronously - * - * @param userIds The users for which to unsuspend packages + * @param suspendingPackage The suspending package + * @param userId The user for which the changes are taking place. */ - void unsuspendForNonSystemSuspendingPackages(ArraySet<Integer> userIds) { - final int sz = userIds.size(); - for (int i = 0; i < sz; i++) { - unsuspendForSuspendingPackages( - (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage), - userIds.valueAt(i)); - } - } - - private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) { - final List<String> affectedPackages = new ArrayList<>(); - final IntArray affectedUids = new IntArray(); + private void unsuspendForSuspendingPackage(String suspendingPackage, int userId) { + final List<String> unsuspendedPackages = new ArrayList<>(); + final IntArray unsuspendedUids = new IntArray(); synchronized (mLock) { for (PackageSetting ps : mSettings.mPackages.values()) { final PackageUserState pus = ps.readUserState(userId); - if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) { - ps.setSuspended(false, null, null, null, null, userId); - affectedPackages.add(ps.name); - affectedUids.add(UserHandle.getUid(userId, ps.getAppId())); + if (pus.suspended) { + ps.removeSuspension(suspendingPackage, userId); + if (!ps.getSuspended(userId)) { + unsuspendedPackages.add(ps.name); + unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId())); + } } } } - if (!affectedPackages.isEmpty()) { - final String[] packageArray = affectedPackages.toArray( - new String[affectedPackages.size()]); - sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId); - sendPackagesSuspendedForUser( - packageArray, affectedUids.toArray(), userId, false, null); - // Write package restrictions immediately to avoid an inconsistent state. - mSettings.writePackageRestrictionsLPr(userId); + if (!unsuspendedPackages.isEmpty()) { + final String[] packageArray = unsuspendedPackages.toArray( + new String[unsuspendedPackages.size()]); + sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId); + sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false); } + // Write package restrictions immediately to avoid an inconsistent state. + mSettings.writePackageRestrictionsLPr(userId); } @Override @@ -18712,10 +18700,7 @@ public class PackageManagerService extends IPackageManager.Stub false /*hidden*/, 0 /*distractionFlags*/, false /*suspended*/, - null /*suspendingPackage*/, - null /*dialogInfo*/, - null /*suspendedAppExtras*/, - null /*suspendedLauncherExtras*/, + null /*suspendParams*/, false /*instantApp*/, false /*virtualPreload*/, null /*lastDisableAppCaller*/, @@ -23217,11 +23202,21 @@ public class PackageManagerService extends IPackageManager.Stub public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) { synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - PersistableBundle launcherExtras = null; + final Bundle allExtras = new Bundle(); if (ps != null) { - launcherExtras = ps.readUserState(userId).suspendedLauncherExtras; + final PackageUserState pus = ps.readUserState(userId); + if (pus.suspended) { + for (int i = 0; i < pus.suspendParams.size(); i++) { + final PackageUserState.SuspendParams params = + pus.suspendParams.valueAt(i); + if (params != null && params.launcherExtras != null) { + allExtras.putAll(params.launcherExtras); + } + } + } + } - return (launcherExtras != null) ? new Bundle(launcherExtras.deepCopy()) : null; + return (allExtras.size() > 0) ? allExtras : null; } } @@ -23237,16 +23232,38 @@ public class PackageManagerService extends IPackageManager.Stub public String getSuspendingPackage(String suspendedPackage, int userId) { synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); - return (ps != null) ? ps.readUserState(userId).suspendingPackage : null; + if (ps != null) { + final PackageUserState pus = ps.readUserState(userId); + if (pus.suspended) { + String suspendingPackage = null; + for (int i = 0; i < pus.suspendParams.size(); i++) { + suspendingPackage = pus.suspendParams.keyAt(i); + if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { + return suspendingPackage; + } + } + return suspendingPackage; + } + } + return null; } } @Override - public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) { + public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, + String suspendingPackage, int userId) { synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); - return (ps != null) ? ps.readUserState(userId).dialogInfo : null; + if (ps != null) { + final PackageUserState pus = ps.readUserState(userId); + if (pus.suspended) { + final PackageUserState.SuspendParams suspendParams = + pus.suspendParams.get(suspendingPackage); + return (suspendParams != null) ? suspendParams.dialogInfo : null; + } + } } + return null; } @Override @@ -23323,7 +23340,6 @@ public class PackageManagerService extends IPackageManager.Stub usersWithPoOrDo.add(profileOwnerPackages.keyAt(i)); } } - unsuspendForNonSystemSuspendingPackages(usersWithPoOrDo); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index fe529a152364..64391c30fa5a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2044,7 +2044,9 @@ class PackageManagerShellCommand extends ShellCommand { } try { mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState, - appExtras, launcherExtras, info, callingPackage, userId); + ((appExtras.size() > 0) ? appExtras : null), + ((launcherExtras.size() > 0) ? launcherExtras : null), + info, callingPackage, userId); pw.println("Package " + packageName + " new suspended state: " + mInterface.isPackageSuspendedForUser(packageName, userId)); return 0; diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 029673ffd87b..0da6b549f296 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -29,6 +29,7 @@ import android.content.pm.Signature; import android.content.pm.SuspendDialogInfo; import android.os.PersistableBundle; import android.service.pm.PackageProto; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; @@ -405,14 +406,28 @@ public abstract class PackageSettingBase extends SettingBase { return readUserState(userId).suspended; } - void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo, + void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) { final PackageUserState existingUserState = modifyUserState(userId); - existingUserState.suspended = suspended; - existingUserState.suspendingPackage = suspended ? suspendingPackage : null; - existingUserState.dialogInfo = suspended ? dialogInfo : null; - existingUserState.suspendedAppExtras = suspended ? appExtras : null; - existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null; + final PackageUserState.SuspendParams newSuspendParams = + PackageUserState.SuspendParams.getInstanceOrNull(dialogInfo, appExtras, + launcherExtras); + if (existingUserState.suspendParams == null) { + existingUserState.suspendParams = new ArrayMap<>(); + } + existingUserState.suspendParams.put(suspendingPackage, newSuspendParams); + existingUserState.suspended = true; + } + + void removeSuspension(String suspendingPackage, int userId) { + final PackageUserState existingUserState = modifyUserState(userId); + if (existingUserState.suspendParams != null) { + existingUserState.suspendParams.remove(suspendingPackage); + if (existingUserState.suspendParams.size() == 0) { + existingUserState.suspendParams = null; + } + } + existingUserState.suspended = (existingUserState.suspendParams != null); } public boolean getInstantApp(int userId) { @@ -433,9 +448,7 @@ public abstract class PackageSettingBase extends SettingBase { void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended, - String suspendingPackage, - SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras, - PersistableBundle suspendedLauncherExtras, boolean instantApp, + ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, int domainVerifState, int linkGeneration, int installReason, @@ -449,10 +462,7 @@ public abstract class PackageSettingBase extends SettingBase { state.hidden = hidden; state.distractionFlags = distractionFlags; state.suspended = suspended; - state.suspendingPackage = suspendingPackage; - state.dialogInfo = dialogInfo; - state.suspendedAppExtras = suspendedAppExtras; - state.suspendedLauncherExtras = suspendedLauncherExtras; + state.suspendParams = suspendParams; state.lastDisableAppCaller = lastDisableAppCaller; state.enabledComponents = enabledComponents; state.disabledComponents = disabledComponents; @@ -467,9 +477,8 @@ public abstract class PackageSettingBase extends SettingBase { void setUserState(int userId, PackageUserState otherState) { setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed, otherState.stopped, otherState.notLaunched, otherState.hidden, - otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage, - otherState.dialogInfo, otherState.suspendedAppExtras, - otherState.suspendedLauncherExtras, otherState.instantApp, + otherState.distractionFlags, otherState.suspended, otherState.suspendParams, + otherState.instantApp, otherState.virtualPreload, otherState.lastDisableAppCaller, otherState.enabledComponents, otherState.disabledComponents, otherState.domainVerificationStatus, otherState.appLinkGeneration, @@ -633,7 +642,10 @@ public abstract class PackageSettingBase extends SettingBase { proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags); proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended); if (state.suspended) { - proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage); + for (int j = 0; j < state.suspendParams.size(); j++) { + proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, + state.suspendParams.keyAt(j)); + } } proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped); proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 597246884ca5..5f9cdb57e5f1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -203,9 +203,22 @@ public final class Settings { private static final String TAG_DEFAULT_BROWSER = "default-browser"; private static final String TAG_DEFAULT_DIALER = "default-dialer"; private static final String TAG_VERSION = "version"; + /** + * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams} + */ + @Deprecated private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info"; + /** + * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams} + */ + @Deprecated private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras"; + /** + * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams} + */ + @Deprecated private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras"; + private static final String TAG_SUSPEND_PARAMS = "suspend-params"; public static final String ATTR_NAME = "name"; public static final String ATTR_PACKAGE = "package"; @@ -658,10 +671,7 @@ public final class Settings { false /*hidden*/, 0 /*distractionFlags*/, false /*suspended*/, - null /*suspendingPackage*/, - null /*dialogInfo*/, - null /*suspendedAppExtras*/, - null /*suspendedLauncherExtras*/, + null /*suspendParams*/, instantApp, virtualPreload, null /*lastDisableAppCaller*/, @@ -1548,10 +1558,7 @@ public final class Settings { false /*hidden*/, 0 /*distractionFlags*/, false /*suspended*/, - null /*suspendingPackage*/, - null /*dialogInfo*/, - null /*suspendedAppExtras*/, - null /*suspendedLauncherExtras*/, + null /*suspendParams*/, false /*instantApp*/, false /*virtualPreload*/, null /*lastDisableAppCaller*/, @@ -1626,12 +1633,12 @@ public final class Settings { ATTR_DISTRACTION_FLAGS, 0); final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED, false); - String suspendingPackage = parser.getAttributeValue(null, + String oldSuspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE); final String dialogMessage = parser.getAttributeValue(null, ATTR_SUSPEND_DIALOG_MESSAGE); - if (suspended && suspendingPackage == null) { - suspendingPackage = PLATFORM_PACKAGE_NAME; + if (suspended && oldSuspendingPackage == null) { + oldSuspendingPackage = PLATFORM_PACKAGE_NAME; } final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser, @@ -1661,9 +1668,10 @@ public final class Settings { ArraySet<String> disabledComponents = null; PersistableBundle suspendedAppExtras = null; PersistableBundle suspendedLauncherExtras = null; - SuspendDialogInfo suspendDialogInfo = null; + SuspendDialogInfo oldSuspendDialogInfo = null; int packageDepth = parser.getDepth(); + ArrayMap<String, PackageUserState.SuspendParams> suspendParamsMap = null; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > packageDepth)) { @@ -1685,26 +1693,48 @@ public final class Settings { suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser); break; case TAG_SUSPENDED_DIALOG_INFO: - suspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser); + oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser); + break; + case TAG_SUSPEND_PARAMS: + final String suspendingPackage = parser.getAttributeValue(null, + ATTR_SUSPENDING_PACKAGE); + if (suspendingPackage == null) { + Slog.wtf(TAG, "No suspendingPackage found inside tag " + + TAG_SUSPEND_PARAMS); + continue; + } + if (suspendParamsMap == null) { + suspendParamsMap = new ArrayMap<>(); + } + suspendParamsMap.put(suspendingPackage, + PackageUserState.SuspendParams.restoreFromXml(parser)); break; default: Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag " + TAG_PACKAGE); } } - if (suspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) { - suspendDialogInfo = new SuspendDialogInfo.Builder() + if (oldSuspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) { + oldSuspendDialogInfo = new SuspendDialogInfo.Builder() .setMessage(dialogMessage) .build(); } + if (suspended && suspendParamsMap == null) { + final PackageUserState.SuspendParams suspendParams = + PackageUserState.SuspendParams.getInstanceOrNull( + oldSuspendDialogInfo, + suspendedAppExtras, + suspendedLauncherExtras); + suspendParamsMap = new ArrayMap<>(); + suspendParamsMap.put(oldSuspendingPackage, suspendParams); + } if (blockUninstall) { setBlockUninstallLPw(userId, name, true); } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, - hidden, distractionFlags, suspended, suspendingPackage, - suspendDialogInfo, - suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload, + hidden, distractionFlags, suspended, suspendParamsMap, + instantApp, virtualPreload, enabledCaller, enabledComponents, disabledComponents, verifState, linkGeneration, installReason, harmfulAppWarning); } else if (tagName.equals("preferred-activities")) { @@ -2004,35 +2034,6 @@ public final class Settings { } if (ustate.suspended) { serializer.attribute(null, ATTR_SUSPENDED, "true"); - if (ustate.suspendingPackage != null) { - serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, - ustate.suspendingPackage); - } - if (ustate.dialogInfo != null) { - serializer.startTag(null, TAG_SUSPENDED_DIALOG_INFO); - ustate.dialogInfo.saveToXml(serializer); - serializer.endTag(null, TAG_SUSPENDED_DIALOG_INFO); - } - if (ustate.suspendedAppExtras != null) { - serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS); - try { - ustate.suspendedAppExtras.saveToXml(serializer); - } catch (XmlPullParserException xmle) { - Slog.wtf(TAG, "Exception while trying to write suspendedAppExtras for " - + pkg + ". Will be lost on reboot", xmle); - } - serializer.endTag(null, TAG_SUSPENDED_APP_EXTRAS); - } - if (ustate.suspendedLauncherExtras != null) { - serializer.startTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS); - try { - ustate.suspendedLauncherExtras.saveToXml(serializer); - } catch (XmlPullParserException xmle) { - Slog.wtf(TAG, "Exception while trying to write suspendedLauncherExtras" - + " for " + pkg + ". Will be lost on reboot", xmle); - } - serializer.endTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS); - } } if (ustate.instantApp) { serializer.attribute(null, ATTR_INSTANT_APP, "true"); @@ -2065,6 +2066,19 @@ public final class Settings { serializer.attribute(null, ATTR_HARMFUL_APP_WARNING, ustate.harmfulAppWarning); } + if (ustate.suspended) { + for (int i = 0; i < ustate.suspendParams.size(); i++) { + final String suspendingPackage = ustate.suspendParams.keyAt(i); + serializer.startTag(null, TAG_SUSPEND_PARAMS); + serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, suspendingPackage); + final PackageUserState.SuspendParams params = + ustate.suspendParams.valueAt(i); + if (params != null) { + params.saveToXml(serializer); + } + serializer.endTag(null, TAG_SUSPEND_PARAMS); + } + } if (!ArrayUtils.isEmpty(ustate.enabledComponents)) { serializer.startTag(null, TAG_ENABLED_COMPONENTS); for (final String name : ustate.enabledComponents) { @@ -4755,13 +4769,6 @@ public final class Settings { pw.print(ps.getHidden(user.id)); pw.print(" suspended="); pw.print(ps.getSuspended(user.id)); - if (ps.getSuspended(user.id)) { - final PackageUserState pus = ps.readUserState(user.id); - pw.print(" suspendingPackage="); - pw.print(pus.suspendingPackage); - pw.print(" dialogInfo="); - pw.print(pus.dialogInfo); - } pw.print(" stopped="); pw.print(ps.getStopped(user.id)); pw.print(" notLaunched="); @@ -4773,6 +4780,23 @@ public final class Settings { pw.print(" virtual="); pw.println(ps.getVirtulalPreload(user.id)); + if (ps.getSuspended(user.id)) { + pw.print(prefix); + pw.println(" Suspend params:"); + final PackageUserState pus = ps.readUserState(user.id); + for (int i = 0; i < pus.suspendParams.size(); i++) { + pw.print(prefix); + pw.print(" suspendingPackage="); + pw.print(pus.suspendParams.keyAt(i)); + final PackageUserState.SuspendParams params = pus.suspendParams.valueAt(i); + if (params != null) { + pw.print(" dialogInfo="); + pw.print(params.dialogInfo); + } + pw.println(); + } + } + String[] overlayPaths = ps.getOverlayPaths(user.id); if (overlayPaths != null && overlayPaths.length > 0) { pw.print(prefix); pw.println(" overlay paths:"); diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 9d08e10c6dea..cc69b5a7205f 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -249,7 +249,8 @@ class ActivityStartInterceptor { if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { return interceptSuspendedByAdminPackage(); } - final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId); + final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, + suspendingPackage, mUserId); mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, suspendingPackage, dialogInfo, mUserId); mCallingPid = mRealCallingPid; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 3fe9b52b8415..15032c5c5cbb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -20,8 +20,8 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.res.Resources.ID_NULL; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; @@ -75,10 +75,11 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SmallTest public class PackageManagerSettingsTests { + private static final String TAG = "PackageManagerSettingsTests"; + private static final String PACKAGE_NAME_1 = "com.android.app1"; private static final String PACKAGE_NAME_2 = "com.android.app2"; private static final String PACKAGE_NAME_3 = "com.android.app3"; - private static final String PACKAGE_NAME_1 = "com.android.app1"; - public static final String TAG = "PackageManagerSettingsTests"; + private static final int TEST_RESOURCE_ID = 2131231283; @Mock PermissionSettings mPermissionSettings; @@ -158,7 +159,7 @@ public class PackageManagerSettingsTests { assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT)); } - private PersistableBundle getPersistableBundle(String packageName, long longVal, + private static PersistableBundle createPersistableBundle(String packageName, long longVal, double doubleVal, boolean boolVal, String textVal) { final PersistableBundle bundle = new PersistableBundle(); bundle.putString(packageName + ".TEXT_VALUE", textVal); @@ -169,8 +170,8 @@ public class PackageManagerSettingsTests { } @Test - public void testReadPackageRestrictions_oldSuspendInfo() { - writePackageRestrictions_oldSuspendInfoXml(0); + public void testReadPackageRestrictions_noSuspendingPackage() { + writePackageRestrictions_noSuspendingPackageXml(0); final Object lock = new Object(); final Context context = InstrumentationRegistry.getTargetContext(); final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock); @@ -181,26 +182,61 @@ public class PackageManagerSettingsTests { final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1); final PackageUserState packageUserState1 = ps1.readUserState(0); assertThat(packageUserState1.suspended, is(true)); - assertThat("android".equals(packageUserState1.suspendingPackage), is(true)); + assertThat(packageUserState1.suspendParams.size(), is(1)); + assertThat(packageUserState1.suspendParams.keyAt(0), is("android")); + assertThat(packageUserState1.suspendParams.valueAt(0), is(nullValue())); final PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2); final PackageUserState packageUserState2 = ps2.readUserState(0); assertThat(packageUserState2.suspended, is(false)); - assertThat(packageUserState2.suspendingPackage, is(nullValue())); + assertThat(packageUserState2.suspendParams, is(nullValue())); } @Test - public void testReadWritePackageRestrictions_newSuspendInfo() { + public void testReadPackageRestrictions_noSuspendParamsMap() { + writePackageRestrictions_noSuspendParamsMapXml(0); + final Object lock = new Object(); + final Context context = InstrumentationRegistry.getTargetContext(); + final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock); + settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); + settingsUnderTest.readPackageRestrictionsLPr(0); + + final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1); + final PackageUserState packageUserState1 = ps1.readUserState(0); + assertThat(packageUserState1.suspended, is(true)); + assertThat(packageUserState1.suspendParams.size(), is(1)); + assertThat(packageUserState1.suspendParams.keyAt(0), is(PACKAGE_NAME_3)); + final PackageUserState.SuspendParams params = packageUserState1.suspendParams.valueAt(0); + assertThat(params, is(notNullValue())); + assertThat(params.appExtras.size(), is(1)); + assertThat(params.appExtras.getString("app_extra_string"), is("value")); + assertThat(params.launcherExtras.size(), is(1)); + assertThat(params.launcherExtras.getLong("launcher_extra_long"), is(4L)); + assertThat(params.dialogInfo, is(notNullValue())); + assertThat(params.dialogInfo.getDialogMessage(), is("Dialog Message")); + assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL)); + assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID)); + assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL)); + assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL)); + } + + @Test + public void testReadWritePackageRestrictions_suspendInfo() { final Context context = InstrumentationRegistry.getTargetContext(); final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object()); final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1); final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2); final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3); - final PersistableBundle appExtras1 = getPersistableBundle( + final PersistableBundle appExtras1 = createPersistableBundle( PACKAGE_NAME_1, 1L, 0.01, true, "appString1"); - final PersistableBundle launcherExtras1 = getPersistableBundle( + final PersistableBundle appExtras2 = createPersistableBundle( + PACKAGE_NAME_2, 2L, 0.02, true, "appString2"); + + final PersistableBundle launcherExtras1 = createPersistableBundle( PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1"); + final PersistableBundle launcherExtras2 = createPersistableBundle( + PACKAGE_NAME_2, 20L, 0.2, false, "launcherString2"); final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder() .setIcon(0x11220001) @@ -208,14 +244,23 @@ public class PackageManagerSettingsTests { .setMessage("1st message") .setNeutralButtonText(0x11220003) .build(); + final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder() + .setIcon(0x22220001) + .setTitle(0x22220002) + .setMessage("2nd message") + .setNeutralButtonText(0x22220003) + .build(); - ps1.setSuspended(true, "suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, 0); + ps1.addOrUpdateSuspension("suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, + 0); + ps1.addOrUpdateSuspension("suspendingPackage2", dialogInfo2, appExtras2, launcherExtras2, + 0); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); - ps2.setSuspended(true, "suspendingPackage2", null, null, null, 0); + ps2.addOrUpdateSuspension("suspendingPackage3", null, appExtras1, null, 0); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); - ps3.setSuspended(false, "irrelevant", dialogInfo1, null, null, 0); + ps3.removeSuspension("irrelevant", 0); settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); settingsUnderTest.writePackageRestrictionsLPr(0); @@ -229,27 +274,39 @@ public class PackageManagerSettingsTests { final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1) .readUserState(0); assertThat(readPus1.suspended, is(true)); - assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1")); - assertThat(readPus1.dialogInfo, equalTo(dialogInfo1)); - assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true)); - assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1), + assertThat(readPus1.suspendParams.size(), is(2)); + + assertThat(readPus1.suspendParams.keyAt(0), is("suspendingPackage1")); + final PackageUserState.SuspendParams params11 = readPus1.suspendParams.valueAt(0); + assertThat(params11, is(notNullValue())); + assertThat(params11.dialogInfo, is(dialogInfo1)); + assertThat(BaseBundle.kindofEquals(params11.appExtras, appExtras1), is(true)); + assertThat(BaseBundle.kindofEquals(params11.launcherExtras, launcherExtras1), + is(true)); + + assertThat(readPus1.suspendParams.keyAt(1), is("suspendingPackage2")); + final PackageUserState.SuspendParams params12 = readPus1.suspendParams.valueAt(1); + assertThat(params12, is(notNullValue())); + assertThat(params12.dialogInfo, is(dialogInfo2)); + assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true)); + assertThat(BaseBundle.kindofEquals(params12.launcherExtras, launcherExtras2), is(true)); final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2) .readUserState(0); assertThat(readPus2.suspended, is(true)); - assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2")); - assertThat(readPus2.dialogInfo, is(nullValue())); - assertThat(readPus2.suspendedAppExtras, is(nullValue())); - assertThat(readPus2.suspendedLauncherExtras, is(nullValue())); + assertThat(readPus2.suspendParams.size(), is(1)); + assertThat(readPus2.suspendParams.keyAt(0), is("suspendingPackage3")); + final PackageUserState.SuspendParams params21 = readPus2.suspendParams.valueAt(0); + assertThat(params21, is(notNullValue())); + assertThat(params21.dialogInfo, is(nullValue())); + assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true)); + assertThat(params21.launcherExtras, is(nullValue())); final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3) .readUserState(0); assertThat(readPus3.suspended, is(false)); - assertThat(readPus3.suspendingPackage, is(nullValue())); - assertThat(readPus3.dialogInfo, is(nullValue())); - assertThat(readPus3.suspendedAppExtras, is(nullValue())); - assertThat(readPus3.suspendedLauncherExtras, is(nullValue())); + assertThat(readPus3.suspendParams, is(nullValue())); } @Test @@ -940,10 +997,10 @@ public class PackageManagerSettingsTests { + "</packages>").getBytes()); } - private void writePackageRestrictions_oldSuspendInfoXml(final int userId) { + private void writePackageRestrictions_noSuspendingPackageXml(final int userId) { writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/" + userId + "/package-restrictions.xml"), - ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<package-restrictions>\n" + " <pkg name=\"" + PACKAGE_NAME_1 + "\" suspended=\"true\" />" + " <pkg name=\"" + PACKAGE_NAME_2 + "\" suspended=\"false\" />" @@ -955,6 +1012,30 @@ public class PackageManagerSettingsTests { .getBytes()); } + private void writePackageRestrictions_noSuspendParamsMapXml(final int userId) { + writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/" + + userId + "/package-restrictions.xml"), + ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<package-restrictions>\n" + + " <pkg name=\"" + PACKAGE_NAME_1 + "\" " + + " suspended=\"true\" suspending-package=\"" + PACKAGE_NAME_3 + "\">\n" + + " <suspended-dialog-info dialogMessage=\"Dialog Message\"" + + " iconResId=\"" + TEST_RESOURCE_ID + "\"/>\n" + + " <suspended-app-extras>\n" + + " <string name=\"app_extra_string\">value</string>\n" + + " </suspended-app-extras>\n" + + " <suspended-launcher-extras>\n" + + " <long name=\"launcher_extra_long\" value=\"4\" />\n" + + " </suspended-launcher-extras>\n" + + " </pkg>\n" + + " <preferred-activities />\n" + + " <persistent-preferred-activities />\n" + + " <crossProfile-intent-filters />\n" + + " <default-apps />\n" + + "</package-restrictions>\n") + .getBytes()); + } + private void writeStoppedPackagesXml() { writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"), ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index 8eaf35f6432f..fc5a0ba1af7b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; import android.os.PersistableBundle; +import android.util.ArrayMap; import android.util.ArraySet; import androidx.test.filters.SmallTest; @@ -175,18 +176,43 @@ public class PackageUserStateTest { assertThat(testUserState03.equals(oldUserState), is(false)); } + private static PackageUserState.SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo, + PersistableBundle appExtras, PersistableBundle launcherExtras) { + PackageUserState.SuspendParams obj = PackageUserState.SuspendParams.getInstanceOrNull( + dialogInfo, appExtras, launcherExtras); + return obj; + } + + private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey, + String sValue, String dKey, double dValue) { + final PersistableBundle result = new PersistableBundle(3); + if (lKey != null) { + result.putLong("com.unit_test." + lKey, lValue); + } + if (sKey != null) { + result.putString("com.unit_test." + sKey, sValue); + } + if (dKey != null) { + result.putDouble("com.unit_test." + dKey, dValue); + } + return result; + } + @Test public void testPackageUserState05() { - PersistableBundle appExtras1 = new PersistableBundle(); - PersistableBundle appExtras2 = new PersistableBundle(); - appExtras1.putInt("appExtraId", 1); - appExtras2.putInt("appExtraId", 2); - PersistableBundle launcherExtras1 = new PersistableBundle(); - PersistableBundle launcherExtras2 = new PersistableBundle(); - launcherExtras1.putString("name", "launcherExtras1"); - launcherExtras2.putString("name", "launcherExtras2"); + final PersistableBundle appExtras1 = createPersistableBundle("appExtraId", 1, null, null, + null, 0); + final PersistableBundle appExtras2 = createPersistableBundle("appExtraId", 2, null, null, + null, 0); + + final PersistableBundle launcherExtras1 = createPersistableBundle(null, 0, "name", + "launcherExtras1", null, 0); + final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name", + "launcherExtras2", null, 0); + final String suspendingPackage1 = "package1"; final String suspendingPackage2 = "package2"; + final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder() .setMessage("dialogMessage1") .build(); @@ -194,38 +220,23 @@ public class PackageUserStateTest { .setMessage("dialogMessage2") .build(); + final ArrayMap<String, PackageUserState.SuspendParams> paramsMap1 = new ArrayMap<>(); + paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1, + launcherExtras1)); + final ArrayMap<String, PackageUserState.SuspendParams> paramsMap2 = new ArrayMap<>(); + paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2, + appExtras2, launcherExtras2)); + + final PackageUserState testUserState1 = new PackageUserState(); testUserState1.suspended = true; - testUserState1.suspendedAppExtras = appExtras1; - testUserState1.suspendedLauncherExtras = launcherExtras1; - testUserState1.suspendingPackage = suspendingPackage1; - testUserState1.dialogInfo = dialogInfo1; + testUserState1.suspendParams = paramsMap1; PackageUserState testUserState2 = new PackageUserState(testUserState1); assertThat(testUserState1.equals(testUserState2), is(true)); - testUserState2.suspendingPackage = suspendingPackage2; - assertThat(testUserState1.equals(testUserState2), is(false)); - - testUserState2 = new PackageUserState(testUserState1); - testUserState2.suspendedAppExtras = appExtras2; - assertThat(testUserState1.equals(testUserState2), is(false)); - - testUserState2 = new PackageUserState(testUserState1); - testUserState2.suspendedLauncherExtras = launcherExtras2; - assertThat(testUserState1.equals(testUserState2), is(false)); - - testUserState2 = new PackageUserState(testUserState1); - testUserState2.dialogInfo = dialogInfo2; + testUserState2.suspendParams = paramsMap2; + // Should not be equal since suspendParams maps are different assertThat(testUserState1.equals(testUserState2), is(false)); - - testUserState2 = new PackageUserState(testUserState1); - testUserState2.suspended = testUserState1.suspended = false; - // Everything is different but irrelevant if suspended is false - testUserState2.suspendingPackage = suspendingPackage2; - testUserState2.dialogInfo = dialogInfo2; - testUserState2.suspendedAppExtras = appExtras2; - testUserState2.suspendedLauncherExtras = launcherExtras2; - assertThat(testUserState1.equals(testUserState2), is(true)); } @Test @@ -243,4 +254,46 @@ public class PackageUserStateTest { assertThat(userState1.equals(userState2), is(false)); } + @Test + public void testPackageUserState07() { + final PersistableBundle appExtras1 = createPersistableBundle("appExtraId", 1, null, null, + null, 0); + final PersistableBundle appExtras2 = createPersistableBundle("appExtraId", 2, null, null, + null, 0); + + final PersistableBundle launcherExtras1 = createPersistableBundle(null, 0, "name", + "launcherExtras1", null, 0); + final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name", + "launcherExtras2", null, 0); + + final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder() + .setMessage("dialogMessage1") + .build(); + final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder() + .setMessage("dialogMessage2") + .build(); + + final PackageUserState.SuspendParams params1; + PackageUserState.SuspendParams params2; + params1 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1); + params2 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1); + // Everything is same + assertThat(params1.equals(params2), is(true)); + + params2 = createSuspendParams(dialogInfo2, appExtras1, launcherExtras1); + // DialogInfo is different + assertThat(params1.equals(params2), is(false)); + + params2 = createSuspendParams(dialogInfo1, appExtras2, launcherExtras1); + // app extras are different + assertThat(params1.equals(params2), is(false)); + + params2 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras2); + // Launcher extras are different + assertThat(params1.equals(params2), is(false)); + + params2 = createSuspendParams(dialogInfo2, appExtras2, launcherExtras2); + // Everything is different + assertThat(params1.equals(params2), is(false)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 350114c792bf..4165052fa675 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -175,8 +175,8 @@ public class ActivityStartInterceptorTest { .build(); when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) .thenReturn(suspendingPackage); - when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID)) - .thenReturn(dialogInfo); + when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage, + TEST_USER_ID)).thenReturn(dialogInfo); // THEN calling intercept returns true assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null)); |