diff options
18 files changed, 444 insertions, 123 deletions
diff --git a/api/current.txt b/api/current.txt index 0d0c4fe53ac6..39ee0ede83a1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11191,6 +11191,7 @@ package android.content.pm { method public void registerCallback(android.content.pm.LauncherApps.Callback); method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler); method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle); + method public boolean shouldHideFromSuggestions(java.lang.String, android.os.UserHandle); method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle); method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle); diff --git a/api/system-current.txt b/api/system-current.txt index f8ebafca0863..47500d97c9a4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1461,6 +1461,7 @@ package android.content.pm { method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int); + method public java.lang.String[] setDistractingPackageRestrictions(java.lang.String[], int); method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence); method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String); method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo); @@ -1528,6 +1529,9 @@ package android.content.pm { field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 + field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 + field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 + field public static final int RESTRICTION_NONE = 0; // 0x0 } public static abstract class PackageManager.DexModuleRegisterCallback { @@ -1535,6 +1539,9 @@ package android.content.pm { method public abstract void onDexModuleRegistered(java.lang.String, boolean, java.lang.String); } + public static abstract class PackageManager.DistractionRestriction implements java.lang.annotation.Annotation { + } + public static abstract interface PackageManager.OnPermissionsChangedListener { method public abstract void onPermissionsChanged(int); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 94983e1c3672..9bcb36f28d17 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2276,6 +2276,16 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public String[] setDistractingPackageRestrictions(String[] packages, int distractionFlags) { + try { + return mPM.setDistractingPackageRestrictionsAsUser(packages, distractionFlags, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override public String[] setPackagesSuspended(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e37126b8c2e7..6086aa6852eb 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2411,6 +2411,25 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED"; /** + * Broadcast Action: Distracting packages have been changed. + * <p>Includes the following extras: + * <ul> + * <li> {@link #EXTRA_CHANGED_PACKAGE_LIST} is the set of packages which have been changed. + * <li> {@link #EXTRA_CHANGED_UID_LIST} is the set of uids which have been changed. + * <li> {@link #EXTRA_DISTRACTION_RESTRICTIONS} the new restrictions set on these packages. + * </ul> + * + * <p class="note">This is a protected intent that can only be sent + * by the system. It is only sent to registered receivers. + * + * @see PackageManager#setDistractingPackageRestrictions(String[], int) + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISTRACTING_PACKAGES_CHANGED = + "android.intent.action.DISTRACTING_PACKAGES_CHANGED"; + + /** * Broadcast Action: Sent to a package that has been suspended by the system. This is sent * whenever a package is put into a suspended state or any of its app extras change while in the * suspended state. @@ -5120,6 +5139,17 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.changed_uid_list"; /** + * An integer denoting a bitwise combination of restrictions set on distracting packages via + * {@link PackageManager#setDistractingPackageRestrictions(String[], int)} + * + * @hide + * @see PackageManager.DistractionRestriction + * @see PackageManager#setDistractingPackageRestrictions(String[], int) + */ + public static final String EXTRA_DISTRACTION_RESTRICTIONS = + "android.intent.extra.distraction_restrictions"; + + /** * @hide * Magic extra system code can use when binding, to give a label for * who it is that has bound to a service. This is an integer giving diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index ba7710b8ef48..db2b6fd235d3 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -69,6 +69,7 @@ interface ILauncherApps { int userId); boolean hasShortcutHostPermission(String callingPackage); + boolean shouldHideFromSuggestions(String packageName, in UserHandle user); ParceledListSlice getShortcutConfigActivities( String callingPackage, String packageName, in UserHandle user); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 64a4479be7ee..d5c3b26b094d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -273,6 +273,9 @@ interface IPackageManager { void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); + String[] setDistractingPackageRestrictionsAsUser(in String[] packageNames, int restrictionFlags, + int userId); + String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, in PersistableBundle appExtras, in PersistableBundle launcherExtras, in SuspendDialogInfo dialogInfo, String callingPackage, int userId); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 44e652f10094..983ea9f847cd 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -697,6 +697,26 @@ public class LauncherApps { } /** + * Returns whether a package should be hidden from suggestions to the user. Currently, this + * could be done because the package was marked as distracting to the user via + * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. + * + * @param packageName The package for which to check. + * @param user the {@link UserHandle} of the profile. + * @return + */ + public boolean shouldHideFromSuggestions(@NonNull String packageName, + @NonNull UserHandle user) { + Preconditions.checkNotNull(packageName, "packageName"); + Preconditions.checkNotNull(user, "user"); + try { + return mService.shouldHideFromSuggestions(packageName, user); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Returns {@link ApplicationInfo} about an application installed for a specific user profile. * * @param packageName The package name of the application diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2aeb68da365b..636a70f040da 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5889,6 +5889,74 @@ public abstract class PackageManager { public abstract boolean isSignedByExactly(String packageName, KeySet ks); /** + * Flag to denote no restrictions. This should be used to clear any restrictions that may have + * been previously set for the package. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_NONE = 0x0; + + /** + * Flag to denote that a package should be hidden from any suggestions to the user. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 0x00000001; + + /** + * Flag to denote that a package's notifications should be hidden. + * @see PackageManager.DistractionRestriction + * @hide + */ + @SystemApi + public static final int RESTRICTION_HIDE_NOTIFICATIONS = 0x00000002; + + /** + * Restriction flags to set on a package that is considered as distracting to the user. + * These should help the user to restrict their usage of these apps. + * + * @see #setDistractingPackageRestrictions(String[], int) + * @hide + */ + @SystemApi + @IntDef(flag = true, prefix = {"RESTRICTION_"}, value = { + RESTRICTION_NONE, + RESTRICTION_HIDE_FROM_SUGGESTIONS, + RESTRICTION_HIDE_NOTIFICATIONS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DistractionRestriction {} + + /** + * Mark or unmark the given packages as distracting to the user. + * These packages can have certain restrictions set that should discourage the user to launch + * them often. For example, notifications from such an app can be hidden, or the app can be + * removed from launcher suggestions, so the user is able to restrict their use of these apps. + * + * <p>The caller must hold {@link android.Manifest.permission#SUSPEND_APPS} to use this API. + * + * @param packages Packages to mark as distracting. + * @param restrictionFlags Any combination of {@link DistractionRestriction restrictions} to + * impose on the given packages. {@link #RESTRICTION_NONE} can be used + * to clear any existing restrictions. + * @return A list of packages that could not have the {@code restrictionFlags} set. The system + * may prevent restricting critical packages to preserve normal device function. + * + * @see DistractionRestriction + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) + @NonNull + public String[] setDistractingPackageRestrictions(@NonNull String[] packages, + @DistractionRestriction int restrictionFlags) { + throw new UnsupportedOperationException( + "setDistractingPackageRestrictions not implemented"); + } + + /** * Puts the package in a suspended state, where attempts at starting activities are denied. * * <p>It doesn't remove the data or the actual package file. The application's notifications @@ -5911,8 +5979,7 @@ public abstract class PackageManager { * {@link PersistableBundle} objects to be shared with the apps being suspended and the * launcher to support customization that they might need to handle the suspended state. * - * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or - * {@link Manifest.permission#MANAGE_USERS} to use this api.</p> + * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API. * * @param packageNames The names of the packages to set the suspended status. * @param suspended If set to {@code true}, the packages will be suspended, if set to @@ -5955,7 +6022,7 @@ public abstract class PackageManager { * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app * is suspended will be shown instead. * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object - * to this api. This dialog will have a button that starts the + * to this API. This dialog will have a button that starts the * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an * activity which handles this action. * @@ -5966,7 +6033,7 @@ public abstract class PackageManager { * {@link PersistableBundle} objects to be shared with the apps being suspended and the * launcher to support customization that they might need to handle the suspended state. * - * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api. + * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API. * * @param packageNames The names of the packages to set the suspended status. * @param suspended If set to {@code true}, the packages will be suspended, if set to @@ -6005,7 +6072,7 @@ public abstract class PackageManager { * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical * packages to keep the device in a functioning state, e.g. the default dialer. - * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api. + * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this API. * * <p> * Note that this set of critical packages can change with time, so even though a package name diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 83979e925be1..83563d0fd9f6 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -284,6 +284,17 @@ public abstract class PackageManagerInternal { public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId); /** + * Gets any distraction flags set via + * {@link PackageManager#setDistractingPackageRestrictions(String[], int)} + * + * @param packageName + * @param userId + * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction} + */ + public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions( + String packageName, int userId); + + /** * Do a straight uid lookup for the given package/application in the given user. * @see PackageManager#getPackageUidAsUser(String, int, int) * @return The app's uid, or < 0 if the package was not found in that user diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 74dd08fc1d6b..249b6919fc28 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -54,6 +54,7 @@ public class PackageUserState { public boolean stopped; public boolean notLaunched; public boolean hidden; // Is the app restricted by owner / admin + public int distractionFlags; public boolean suspended; public String suspendingPackage; public SuspendDialogInfo dialogInfo; @@ -92,6 +93,7 @@ public class PackageUserState { stopped = o.stopped; notLaunched = o.notLaunched; hidden = o.hidden; + distractionFlags = o.distractionFlags; suspended = o.suspended; suspendingPackage = o.suspendingPackage; dialogInfo = o.dialogInfo; @@ -222,6 +224,9 @@ public class PackageUserState { if (hidden != oldState.hidden) { return false; } + if (distractionFlags != oldState.distractionFlags) { + return false; + } if (suspended != oldState.suspended) { return false; } diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index 4ecf52ce5012..7f96d701cdbf 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -111,6 +111,7 @@ message PackageProto { optional EnabledState enabled_state = 7; optional string last_disabled_app_caller = 8; optional string suspending_package = 9; + optional int32 distraction_flags = 10; } // Name of package. e.g. "com.android.providers.telephony". diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8147b4a2a009..506d7f2f5353 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -51,6 +51,7 @@ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" /> <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" /> + <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" /> <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" /> <protected-broadcast android:name="android.intent.action.UID_REMOVED" /> <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" /> diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index eb3017db7340..b6dae1977262 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -304,6 +304,17 @@ public class LauncherAppsService extends SystemService { } @Override + public boolean shouldHideFromSuggestions(String packageName, UserHandle user) { + if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) { + return false; + } + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier()); + return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0; + } + + @Override public ParceledListSlice<ResolveInfo> getLauncherActivities(String callingPackage, String packageName, UserHandle user) throws RemoteException { ParceledListSlice<ResolveInfo> launcherActivities = queryActivitiesForUser( diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 597f5b3f4e05..d1a67bbaf845 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -84,6 +84,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING; import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; @@ -12657,22 +12658,30 @@ public class PackageManagerService extends IPackageManager.Stub info.sendPackageRemovedBroadcasts(true /*killApp*/); } + private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId, + int distractionFlags) { + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras, + Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null); + } + private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId, boolean suspended, PersistableBundle launcherExtras) { - if (pkgList.length > 0) { - Bundle extras = new Bundle(1); - 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, - null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, - new int[] {userId}, null); + 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, + null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, + new int[] {userId}, null); } /** @@ -12822,6 +12831,62 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, + int restrictionFlags, int userId) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, + "setPackagesSuspendedAsUser"); + + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID + && UserHandle.getUserId(callingUid) != userId) { + throw new SecurityException("Calling uid " + callingUid + " cannot call for user " + + userId); + } + Preconditions.checkNotNull(packageNames, "packageNames cannot be null"); + + final List<String> changedPackagesList = new ArrayList<>(packageNames.length); + final IntArray changedUids = new IntArray(packageNames.length); + final List<String> unactionedPackages = new ArrayList<>(packageNames.length); + + for (int i = 0; i < packageNames.length; i++) { + final String packageName = packageNames[i]; + final PackageSetting pkgSetting; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + Slog.w(TAG, "Could not find package setting for package: " + packageName + + ". Skipping..."); + unactionedPackages.add(packageName); + continue; + } + } + if (restrictionFlags != 0 && !canSuspendPackageForUserInternal(packageName, userId)) { + unactionedPackages.add(packageName); + continue; + } + synchronized (mPackages) { + final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId); + if (restrictionFlags != oldDistractionFlags) { + pkgSetting.setDistractionFlags(restrictionFlags, userId); + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); + } + } + } + + if (!changedPackagesList.isEmpty()) { + final String[] changedPackages = changedPackagesList.toArray( + new String[changedPackagesList.size()]); + sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, + restrictionFlags); + synchronized (mPackages) { + scheduleWritePackageRestrictionsLocked(userId); + } + } + return unactionedPackages.toArray(new String[0]); + } + + @Override public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo, String callingPackage, int userId) { @@ -12846,44 +12911,37 @@ public class PackageManagerService extends IPackageManager.Stub final List<String> changedPackagesList = new ArrayList<>(packageNames.length); final IntArray changedUids = new IntArray(packageNames.length); final List<String> unactionedPackages = new ArrayList<>(packageNames.length); - final long callingId = Binder.clearCallingIdentity(); - try { - for (int i = 0; i < packageNames.length; i++) { - final String packageName = packageNames[i]; - if (callingPackage.equals(packageName)) { - Slog.w(TAG, "Calling package: " + callingPackage + " trying to " - + (suspended ? "" : "un") + "suspend itself. Ignoring"); - unactionedPackages.add(packageName); - continue; - } - PackageSetting pkgSetting; - synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null - || filterAppAccessLPr(pkgSetting, callingUid, userId)) { - Slog.w(TAG, "Could not find package setting for package: " + packageName - + ". Skipping suspending/un-suspending."); - unactionedPackages.add(packageName); - continue; - } - } - if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) { + + for (int i = 0; i < packageNames.length; i++) { + final String packageName = packageNames[i]; + if (callingPackage.equals(packageName)) { + Slog.w(TAG, "Calling package: " + callingPackage + " trying to " + + (suspended ? "" : "un") + "suspend itself. Ignoring"); + unactionedPackages.add(packageName); + continue; + } + final PackageSetting pkgSetting; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + Slog.w(TAG, "Could not find package setting for package: " + packageName + + ". Skipping suspending/un-suspending."); unactionedPackages.add(packageName); continue; } - synchronized (mPackages) { - pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting != null) { - pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, - launcherExtras, userId); - } - } - changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } - } finally { - Binder.restoreCallingIdentity(callingId); + if (suspended && !canSuspendPackageForUserInternal(packageName, userId)) { + unactionedPackages.add(packageName); + continue; + } + synchronized (mPackages) { + pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, + launcherExtras, userId); + } + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, pkgSetting.appId)); } + if (!changedPackagesList.isEmpty()) { final String[] changedPackages = changedPackagesList.toArray( new String[changedPackagesList.size()]); @@ -13035,88 +13093,87 @@ public class PackageManagerService extends IPackageManager.Stub + " cannot query getUnsuspendablePackagesForUser for user " + userId); } final ArraySet<String> unactionablePackages = new ArraySet<>(); - final long identity = Binder.clearCallingIdentity(); - try { - for (String packageName : packageNames) { - if (!canSuspendPackageForUserInternal(packageName, userId)) { - unactionablePackages.add(packageName); - } + for (String packageName : packageNames) { + if (!canSuspendPackageForUserInternal(packageName, userId)) { + unactionablePackages.add(packageName); } - } finally { - Binder.restoreCallingIdentity(identity); } return unactionablePackages.toArray(new String[unactionablePackages.size()]); } private boolean canSuspendPackageForUserInternal(String packageName, int userId) { - if (isPackageDeviceAdmin(packageName, userId)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": has an active device admin"); - return false; - } - - String activeLauncherPackageName = getActiveLauncherPackageName(userId); - if (packageName.equals(activeLauncherPackageName)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": contains the active launcher"); - return false; - } - - if (packageName.equals(mRequiredInstallerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package installation"); - return false; - } + final long callingId = Binder.clearCallingIdentity(); + try { + if (isPackageDeviceAdmin(packageName, userId)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": has an active device admin"); + return false; + } - if (packageName.equals(mRequiredUninstallerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package uninstallation"); - return false; - } + String activeLauncherPackageName = getActiveLauncherPackageName(userId); + if (packageName.equals(activeLauncherPackageName)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": contains the active launcher"); + return false; + } - if (packageName.equals(mRequiredVerifierPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for package verification"); - return false; - } + if (packageName.equals(mRequiredInstallerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package installation"); + return false; + } - if (packageName.equals(getDefaultDialerPackageName(userId))) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": is the default dialer"); - return false; - } + if (packageName.equals(mRequiredUninstallerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package uninstallation"); + return false; + } - if (packageName.equals(mRequiredPermissionControllerPackage)) { - Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": required for permissions management"); - return false; - } + if (packageName.equals(mRequiredVerifierPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for package verification"); + return false; + } - synchronized (mPackages) { - if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + if (packageName.equals(getDefaultDialerPackageName(userId))) { Slog.w(TAG, "Cannot suspend package \"" + packageName - + "\": protected package"); + + "\": is the default dialer"); return false; } - // Cannot suspend static shared libs as they are considered - // a part of the using app (emulating static linking). Also - // static libs are installed always on internal storage. - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) { - Slog.w(TAG, "Cannot suspend package: " + packageName - + " providing static shared library: " - + pkg.staticSharedLibName); + if (packageName.equals(mRequiredPermissionControllerPackage)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": required for permissions management"); return false; } - } - if (PLATFORM_PACKAGE_NAME.equals(packageName)) { - Slog.w(TAG, "Cannot suspend the platform package: " + packageName); - return false; - } + synchronized (mPackages) { + if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": protected package"); + return false; + } - return true; + // Cannot suspend static shared libs as they are considered + // a part of the using app (emulating static linking). Also + // static libs are installed always on internal storage. + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) { + Slog.w(TAG, "Cannot suspend package: " + packageName + + " providing static shared library: " + + pkg.staticSharedLibName); + return false; + } + } + + if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + Slog.w(TAG, "Cannot suspend the platform package: " + packageName); + return false; + } + return true; + } finally { + Binder.restoreCallingIdentity(callingId); + } } private String getActiveLauncherPackageName(int userId) { @@ -18425,6 +18482,7 @@ public class PackageManagerService extends IPackageManager.Stub true /*stopped*/, true /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -23240,6 +23298,14 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public int getDistractingPackageRestrictions(String packageName, int userId) { + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE; + } + } + + @Override public int getPackageUid(String packageName, int flags, int userId) { return PackageManagerService.this .getPackageUid(packageName, flags, userId); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 3c22f07ad108..58f262c4c889 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -392,6 +392,14 @@ public abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).hidden = hidden; } + int getDistractionFlags(int userId) { + return readUserState(userId).distractionFlags; + } + + void setDistractionFlags(int distractionFlags, int userId) { + modifyUserState(userId).distractionFlags = distractionFlags; + } + boolean getSuspended(int userId) { return readUserState(userId).suspended; } @@ -423,7 +431,8 @@ public abstract class PackageSettingBase extends SettingBase { } void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, - boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage, + boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended, + String suspendingPackage, SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, @@ -437,6 +446,7 @@ public abstract class PackageSettingBase extends SettingBase { state.stopped = stopped; state.notLaunched = notLaunched; state.hidden = hidden; + state.distractionFlags = distractionFlags; state.suspended = suspended; state.suspendingPackage = suspendingPackage; state.dialogInfo = dialogInfo; @@ -607,6 +617,7 @@ public abstract class PackageSettingBase extends SettingBase { } proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType); proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden); + 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); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c524dba01ba6..95da2091828d 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -223,6 +223,7 @@ public final class Settings { private static final String ATTR_BLOCKED = "blocked"; // New name for the above attribute. private static final String ATTR_HIDDEN = "hidden"; + private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags"; private static final String ATTR_SUSPENDED = "suspended"; private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; /** @@ -734,6 +735,7 @@ public final class Settings { true /*stopped*/, true /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -1628,6 +1630,7 @@ public final class Settings { false /*stopped*/, false /*notLaunched*/, false /*hidden*/, + 0 /*distractionFlags*/, false /*suspended*/, null /*suspendingPackage*/, null /*dialogInfo*/, @@ -1703,6 +1706,8 @@ public final class Settings { hidden = hiddenStr == null ? hidden : Boolean.parseBoolean(hiddenStr); + final int distractionFlags = XmlUtils.readIntAttribute(parser, + ATTR_DISTRACTION_FLAGS, 0); final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED, false); String suspendingPackage = parser.getAttributeValue(null, @@ -1781,7 +1786,8 @@ public final class Settings { setBlockUninstallLPw(userId, name, true); } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, - hidden, suspended, suspendingPackage, suspendDialogInfo, + hidden, distractionFlags, suspended, suspendingPackage, + suspendDialogInfo, suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller, enabledComponents, disabledComponents, verifState, linkGeneration, installReason, harmfulAppWarning); @@ -2089,6 +2095,10 @@ public final class Settings { if (ustate.hidden) { serializer.attribute(null, ATTR_HIDDEN, "true"); } + if (ustate.distractionFlags != 0) { + serializer.attribute(null, ATTR_DISTRACTION_FLAGS, + Integer.toString(ustate.distractionFlags)); + } if (ustate.suspended) { serializer.attribute(null, ATTR_SUSPENDED, "true"); if (ustate.suspendingPackage != null) { 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 517b5ade44b8..6d28ed19af4f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -35,6 +35,7 @@ import static org.junit.Assert.fail; import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; @@ -226,8 +227,8 @@ public class PackageManagerSettingsTests { settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3)); // now read and verify settingsUnderTest.readPackageRestrictionsLPr(0); - final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1). - readUserState(0); + 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)); @@ -235,16 +236,16 @@ public class PackageManagerSettingsTests { assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1), is(true)); - final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2). - readUserState(0); + 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())); - final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3). - readUserState(0); + 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())); @@ -254,11 +255,59 @@ public class PackageManagerSettingsTests { @Test public void testPackageRestrictionsSuspendedDefault() { - final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); + final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); assertThat(defaultSetting.getSuspended(0), is(false)); } @Test + public void testReadWritePackageRestrictions_distractionFlags() { + 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 int distractionFlags1 = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + ps1.setDistractionFlags(distractionFlags1, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); + + final int distractionFlags2 = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS + | PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + ps2.setDistractionFlags(distractionFlags2, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); + + final int distractionFlags3 = PackageManager.RESTRICTION_NONE; + ps3.setDistractionFlags(distractionFlags3, 0); + settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); + + settingsUnderTest.writePackageRestrictionsLPr(0); + + settingsUnderTest.mPackages.clear(); + settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1)); + settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2)); + settingsUnderTest.mPackages.put(PACKAGE_NAME_3, createPackageSetting(PACKAGE_NAME_3)); + // now read and verify + settingsUnderTest.readPackageRestrictionsLPr(0); + final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1) + .readUserState(0); + assertThat(readPus1.distractionFlags, is(distractionFlags1)); + + final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2) + .readUserState(0); + assertThat(readPus2.distractionFlags, is(distractionFlags2)); + + final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3) + .readUserState(0); + assertThat(readPus3.distractionFlags, is(distractionFlags3)); + } + + @Test + public void testPackageRestrictionsDistractionFlagsDefault() { + final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1); + assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE)); + } + + @Test public void testEnableDisable() { // Write the package files and make sure they're parsed properly the first time writeOldFiles(); @@ -692,6 +741,7 @@ public class PackageManagerSettingsTests { assertThat(userState.notLaunched, is(notLaunched)); assertThat(userState.stopped, is(stopped)); assertThat(userState.suspended, is(false)); + assertThat(userState.distractionFlags, is(0)); if (oldUserState != null) { assertThat(userState.equals(oldUserState), is(not(userStateChanged))); } 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 f0ed612400ed..8eaf35f6432f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -22,6 +22,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import android.content.pm.PackageManager; import android.content.pm.PackageUserState; import android.content.pm.SuspendDialogInfo; import android.os.PersistableBundle; @@ -227,4 +228,19 @@ public class PackageUserStateTest { assertThat(testUserState1.equals(testUserState2), is(true)); } + @Test + public void testPackageUserState06() { + final PackageUserState userState1 = new PackageUserState(); + assertThat(userState1.distractionFlags, is(PackageManager.RESTRICTION_NONE)); + userState1.distractionFlags = PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS; + + final PackageUserState copyOfUserState1 = new PackageUserState(userState1); + assertThat(userState1.distractionFlags, is(copyOfUserState1.distractionFlags)); + assertThat(userState1.equals(copyOfUserState1), is(true)); + + final PackageUserState userState2 = new PackageUserState(userState1); + userState2.distractionFlags = PackageManager.RESTRICTION_HIDE_NOTIFICATIONS; + assertThat(userState1.equals(userState2), is(false)); + } + } |