summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt7
-rw-r--r--core/java/android/app/ApplicationPackageManager.java10
-rw-r--r--core/java/android/content/Intent.java30
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl1
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/LauncherApps.java20
-rw-r--r--core/java/android/content/pm/PackageManager.java77
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java11
-rw-r--r--core/java/android/content/pm/PackageUserState.java5
-rw-r--r--core/proto/android/service/package.proto1
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java284
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java13
-rw-r--r--services/core/java/com/android/server/pm/Settings.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java16
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));
+ }
+
}