summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java3
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java236
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java13
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java26
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java185
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java22
7 files changed, 374 insertions, 119 deletions
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 26240c572898..c1a3c2b5f150 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -283,6 +283,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
throw ex.rethrowFromSystemServer();
+ } catch (SecurityException e) {
+ // app cannot catch and recover from this, so do on their behalf
+ Log.w(TAG, "Enqueue adjustment failed; no longer connected", e);
}
}
break;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 7751f5f9bcb8..8d9c66cdb803 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -20,6 +20,7 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.os.UserHandle.USER_ALL;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -53,12 +54,15 @@ import android.service.notification.ManagedServicesProto.ServiceProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -72,6 +76,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
@@ -138,10 +143,6 @@ abstract public class ManagedServices {
// not mean that we are currently bound to said package/component.
private ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>();
- // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
- // user change).
- private int[] mLastSeenProfileIds;
-
// True if approved services are stored in xml, not settings.
private boolean mUseXml;
@@ -296,7 +297,7 @@ abstract public class ManagedServices {
Settings.Secure.putStringForUser(
mContext.getContentResolver(), element, value, userId);
loadAllowedComponentsFromSettings();
- rebindServices(false);
+ rebindServices(false, userId);
}
}
}
@@ -381,7 +382,7 @@ abstract public class ManagedServices {
}
}
}
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
protected void upgradeXml(final int xmlVersion, final int userId) {}
@@ -456,7 +457,7 @@ abstract public class ManagedServices {
}
}
- rebindServices(false);
+ rebindServices(false, userId);
}
private String getApprovedValue(String pkgOrComponent) {
@@ -574,7 +575,7 @@ abstract public class ManagedServices {
if (anyServicesInvolved) {
// make sure we're still bound to any of our services who may have just upgraded
- rebindServices(false);
+ rebindServices(false, USER_ALL);
}
}
}
@@ -582,21 +583,17 @@ abstract public class ManagedServices {
public void onUserRemoved(int user) {
Slog.i(TAG, "Removing approved services for removed user " + user);
mApproved.remove(user);
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
- if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
- if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices().");
- return;
- }
- rebindServices(true);
+ rebindServices(true, user);
}
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(false);
+ rebindServices(false, user);
}
private ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
@@ -690,9 +687,10 @@ abstract public class ManagedServices {
component.flattenToShortString());
synchronized (mMutex) {
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
+ final IntArray userIds = mUserProfiles.getCurrentProfileIds();
- for (int userId : userIds) {
+ for (int i = 0; i < userIds.size(); i++) {
+ final int userId = userIds.get(i);
if (enabled) {
if (isPackageOrComponentAllowed(component.toString(), userId)
|| isPackageOrComponentAllowed(component.getPackageName(), userId)) {
@@ -834,20 +832,14 @@ abstract public class ManagedServices {
return false;
}
- /**
- * Called whenever packages change, the user switches, or the secure setting
- * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
- */
- protected void rebindServices(boolean forceRebind) {
- if (DEBUG) Slog.d(TAG, "rebindServices");
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- final int nUserIds = userIds.length;
-
+ @VisibleForTesting
+ protected SparseArray<ArraySet<ComponentName>> getAllowedComponents(IntArray userIds) {
+ final int nUserIds = userIds.size();
final SparseArray<ArraySet<ComponentName>> componentsByUser = new SparseArray<>();
for (int i = 0; i < nUserIds; ++i) {
- final int userId = userIds[i];
- final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userIds[i]);
+ final int userId = userIds.get(i);
+ final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
if (approvedLists != null) {
final int N = approvedLists.size();
for (int j = 0; j < N; j++) {
@@ -861,67 +853,125 @@ abstract public class ManagedServices {
}
}
}
+ return componentsByUser;
+ }
- final ArrayList<ManagedServiceInfo> removableBoundServices = new ArrayList<>();
- final SparseArray<Set<ComponentName>> toAdd = new SparseArray<>();
+ @GuardedBy("mMutex")
+ protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
+ final IntArray activeUsers,
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
+ mEnabledServicesForCurrentProfiles.clear();
+ mEnabledServicesPackageNames.clear();
+ final int nUserIds = activeUsers.size();
- synchronized (mMutex) {
- // Rebind to non-system services if user switched
- for (ManagedServiceInfo service : mServices) {
- if (!service.isSystem && !service.isGuest(this)) {
- removableBoundServices.add(service);
- }
+ for (int i = 0; i < nUserIds; ++i) {
+ // decode the list of components
+ final int userId = activeUsers.get(i);
+ final ArraySet<ComponentName> userComponents = approvedComponentsByUser.get(userId);
+ if (null == userComponents) {
+ componentsToBind.put(userId, new ArraySet<>());
+ continue;
}
- mEnabledServicesForCurrentProfiles.clear();
- mEnabledServicesPackageNames.clear();
+ final Set<ComponentName> add = new HashSet<>(userComponents);
+ add.removeAll(mSnoozingForCurrentProfiles);
- for (int i = 0; i < nUserIds; ++i) {
- // decode the list of components
- final ArraySet<ComponentName> userComponents = componentsByUser.get(userIds[i]);
- if (null == userComponents) {
- toAdd.put(userIds[i], new ArraySet<>());
- continue;
- }
-
- final Set<ComponentName> add = new HashSet<>(userComponents);
- add.removeAll(mSnoozingForCurrentProfiles);
+ componentsToBind.put(userId, add);
- toAdd.put(userIds[i], add);
+ mEnabledServicesForCurrentProfiles.addAll(userComponents);
- mEnabledServicesForCurrentProfiles.addAll(userComponents);
+ for (int j = 0; j < userComponents.size(); j++) {
+ final ComponentName component = userComponents.valueAt(j);
+ mEnabledServicesPackageNames.add(component.getPackageName());
+ }
+ }
+ }
- for (int j = 0; j < userComponents.size(); j++) {
- final ComponentName component = userComponents.valueAt(j);
- mEnabledServicesPackageNames.add(component.getPackageName());
- }
+ @GuardedBy("mMutex")
+ protected Set<ManagedServiceInfo> getRemovableConnectedServices() {
+ final Set<ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ for (ManagedServiceInfo service : mServices) {
+ if (!service.isSystem && !service.isGuest(this)) {
+ removableBoundServices.add(service);
}
}
+ return removableBoundServices;
+ }
+ protected void populateComponentsToUnbind(
+ boolean forceRebind,
+ Set<ManagedServiceInfo> removableBoundServices,
+ SparseArray<Set<ComponentName>> allowedComponentsToBind,
+ SparseArray<Set<ComponentName>> componentsToUnbind) {
for (ManagedServiceInfo info : removableBoundServices) {
- final ComponentName component = info.component;
- final int oldUser = info.userid;
- final Set<ComponentName> allowedComponents = toAdd.get(info.userid);
+ final Set<ComponentName> allowedComponents = allowedComponentsToBind.get(info.userid);
if (allowedComponents != null) {
- if (allowedComponents.contains(component) && !forceRebind) {
- // Already bound, don't need to bind again.
- allowedComponents.remove(component);
- } else {
- // No longer allowed to be bound, or must rebind.
- Slog.v(TAG, "disabling " + getCaption() + " for user "
- + oldUser + ": " + component);
- unregisterService(component, oldUser);
+ if (forceRebind || !allowedComponents.contains(info.component)) {
+ Set<ComponentName> toUnbind =
+ componentsToUnbind.get(info.userid, new ArraySet<>());
+ toUnbind.add(info.component);
+ componentsToUnbind.put(info.userid, toUnbind);
}
}
}
+ }
- for (int i = 0; i < nUserIds; ++i) {
- final Set<ComponentName> add = toAdd.get(userIds[i]);
+ /**
+ * Called whenever packages change, the user switches, or the secure setting
+ * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+ */
+ protected void rebindServices(boolean forceRebind, int userToRebind) {
+ if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind);
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
+ if (userToRebind != USER_ALL) {
+ userIds = new IntArray(1);
+ userIds.add(userToRebind);
+ }
+
+ final SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ synchronized (mMutex) {
+ final SparseArray<ArraySet<ComponentName>> approvedComponentsByUser =
+ getAllowedComponents(userIds);
+ final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+
+ // Filter approvedComponentsByUser to collect all of the components that are allowed
+ // for the currently active user(s).
+ populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+
+ // For every current non-system connection, disconnect services that are no longer
+ // approved, or ALL services if we are force rebinding
+ populateComponentsToUnbind(
+ forceRebind, removableBoundServices, componentsToBind, componentsToUnbind);
+ }
+
+ unbindFromServices(componentsToUnbind);
+ bindToServices(componentsToBind);
+ }
+
+ protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
+ for (int i = 0; i < componentsToUnbind.size(); i++) {
+ final int userId = componentsToUnbind.keyAt(i);
+ final Set<ComponentName> removableComponents = componentsToUnbind.get(userId);
+ for (ComponentName cn : removableComponents) {
+ // No longer allowed to be bound, or must rebind.
+ Slog.v(TAG, "disabling " + getCaption() + " for user " + userId + ": " + cn);
+ unregisterService(cn, userId);
+ }
+ }
+ }
+
+ // Attempt to bind to services, skipping those that cannot be found or lack the permission.
+ private void bindToServices(SparseArray<Set<ComponentName>> componentsToBind) {
+ for (int i = 0; i < componentsToBind.size(); i++) {
+ final int userId = componentsToBind.keyAt(i);
+ final Set<ComponentName> add = componentsToBind.get(userId);
for (ComponentName component : add) {
try {
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
if (info == null) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": service not found");
@@ -933,15 +983,13 @@ abstract public class ManagedServices {
continue;
}
Slog.v(TAG,
- "enabling " + getCaption() + " for " + userIds[i] + ": " + component);
- registerService(component, userIds[i]);
+ "enabling " + getCaption() + " for " + userId + ": " + component);
+ registerService(component, userId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
-
- mLastSeenProfileIds = userIds;
}
/**
@@ -1018,7 +1066,7 @@ abstract public class ManagedServices {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
- Slog.v(TAG, getCaption() + " service connected: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " service connected: " + name);
boolean added = false;
ManagedServiceInfo info = null;
synchronized (mMutex) {
@@ -1040,12 +1088,12 @@ abstract public class ManagedServices {
@Override
public void onServiceDisconnected(ComponentName name) {
- Slog.v(TAG, getCaption() + " connection lost: " + name);
+ Slog.v(TAG, userid + " " + getCaption() + " connection lost: " + name);
}
@Override
public void onBindingDied(ComponentName name) {
- Slog.w(TAG, getCaption() + " binding died: " + name);
+ Slog.w(TAG, userid + " " + getCaption() + " binding died: " + name);
synchronized (mMutex) {
unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
@@ -1057,8 +1105,8 @@ abstract public class ManagedServices {
}
}, ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
- Slog.v(TAG, getCaption() + " not rebinding as "
- + "a previous rebind attempt was made: " + name);
+ Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+ + " as a previous rebind attempt was made: " + name);
}
}
}
@@ -1068,7 +1116,8 @@ abstract public class ManagedServices {
BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
new UserHandle(userid))) {
mServicesBound.remove(servicesBindingTag);
- Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+ Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent
+ + " in user " + userid);
return;
}
} catch (SecurityException ex) {
@@ -1232,9 +1281,9 @@ abstract public class ManagedServices {
if (!isEnabledForCurrentProfiles()) {
return false;
}
- if (this.userid == UserHandle.USER_ALL) return true;
+ if (this.userid == USER_ALL) return true;
if (this.isSystem) return true;
- if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+ if (nid == USER_ALL || nid == this.userid) return true;
return supportsProfiles()
&& mUserProfiles.isCurrentProfile(nid)
&& isPermittedForProfile(nid);
@@ -1280,6 +1329,24 @@ abstract public class ManagedServices {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ManagedServiceInfo that = (ManagedServiceInfo) o;
+ return userid == that.userid
+ && isSystem == that.isSystem
+ && targetSdkVersion == that.targetSdkVersion
+ && Objects.equals(service, that.service)
+ && Objects.equals(component, that.component)
+ && Objects.equals(connection, that.connection);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(service, component, userid, isSystem, connection, targetSdkVersion);
+ }
}
/** convenience method for looking in mEnabledServicesForCurrentProfiles */
@@ -1305,12 +1372,15 @@ abstract public class ManagedServices {
}
}
- public int[] getCurrentProfileIds() {
+ /**
+ * Returns the currently active users (generally one user and its work profile).
+ */
+ public IntArray getCurrentProfileIds() {
synchronized (mCurrentProfiles) {
- int[] users = new int[mCurrentProfiles.size()];
+ IntArray users = new IntArray(mCurrentProfiles.size());
final int N = mCurrentProfiles.size();
for (int i = 0; i < N; ++i) {
- users[i] = mCurrentProfiles.keyAt(i);
+ users.add(mCurrentProfiles.keyAt(i));
}
return users;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 60597b58ec52..d522dbae29de 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -174,6 +174,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -1562,7 +1563,7 @@ public class NotificationManagerService extends SystemService {
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext().registerReceiver(mIntentReceiver, filter);
+ getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -1694,10 +1695,10 @@ public class NotificationManagerService extends SystemService {
UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
null);
if (isUidSystemOrPhone(uid)) {
- int[] profileIds = mUserProfiles.getCurrentProfileIds();
- int N = profileIds.length;
+ IntArray profileIds = mUserProfiles.getCurrentProfileIds();
+ int N = profileIds.size();
for (int i = 0; i < N; i++) {
- int profileId = profileIds[i];
+ int profileId = profileIds.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
profileId, REASON_CHANNEL_BANNED,
null);
@@ -6693,7 +6694,9 @@ public class NotificationManagerService extends SystemService {
@Override
public void onUserUnlocked(int user) {
if (DEBUG) Slog.d(TAG, "onUserUnlocked u=" + user);
- rebindServices(true);
+ // force rebind the assistant, as it might be keeping its own state in user locked
+ // storage
+ rebindServices(true, user);
}
protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index a178a525cede..2b581d601ad5 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -15,17 +15,8 @@
*/
package com.android.server.notification;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.NonNull;
import android.app.AlarmManager;
-import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -37,9 +28,18 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -105,12 +105,12 @@ public class SnoozeHelper {
protected @NonNull List<NotificationRecord> getSnoozed() {
List<NotificationRecord> snoozedForUser = new ArrayList<>();
- int[] userIds = mUserProfiles.getCurrentProfileIds();
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
if (userIds != null) {
- final int N = userIds.length;
+ final int N = userIds.size();
for (int i = 0; i < N; i++) {
final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
- mSnoozedNotifications.get(userIds[i]);
+ mSnoozedNotifications.get(userIds.get(i));
if (snoozedPkgs != null) {
final int M = snoozedPkgs.size();
for (int j = 0; j < M; j++) {
@@ -179,7 +179,7 @@ public class SnoozeHelper {
protected boolean cancel(int userId, boolean includeCurrentProfiles) {
int[] userIds = {userId};
if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds();
+ userIds = mUserProfiles.getCurrentProfileIds().toArray();
}
final int N = userIds.length;
for (int i = 0; i < N; i++) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 95d4a15b5fdb..659c6e7aeed3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -20,6 +20,7 @@ import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAG
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -34,6 +35,7 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,6 +49,9 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -68,7 +73,9 @@ import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Set;
public class ManagedServicesTest extends UiServiceTestCase {
@@ -113,7 +120,12 @@ public class ManagedServicesTest extends UiServiceTestCase {
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
when(mUm.getUsers()).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
mExpectedPrimaryPackages = new ArrayMap<>();
mExpectedPrimaryPackages.put(0, "this.is.a.package.name:another.package");
@@ -165,12 +177,12 @@ public class ManagedServicesTest extends UiServiceTestCase {
@Test
public void testBackupAndRestore_migration_preO() throws Exception {
- ArrayMap backupPrimaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryPackages = new ArrayMap<>();
backupPrimaryPackages.put(0, "backup.0:backup:0a");
backupPrimaryPackages.put(10, "10.backup");
backupPrimaryPackages.put(11, "eleven");
backupPrimaryPackages.put(12, "");
- ArrayMap backupPrimaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupPrimaryComponentNames = new ArrayMap<>();
backupPrimaryComponentNames.put(0, "backup.first/whatever:a/b");
backupPrimaryComponentNames.put(10, "again/M1");
backupPrimaryComponentNames.put(11, "orange/youglad:itisnot/banana");
@@ -179,11 +191,11 @@ public class ManagedServicesTest extends UiServiceTestCase {
backupPrimary.put(APPROVAL_BY_PACKAGE, backupPrimaryPackages);
backupPrimary.put(APPROVAL_BY_COMPONENT, backupPrimaryComponentNames);
- ArrayMap backupSecondaryComponentNames = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryComponentNames = new ArrayMap<>();
backupSecondaryComponentNames.put(0, "secondary.1/component.Name");
backupSecondaryComponentNames.put(10,
"this.is.another.package.backup/with.Component:component.backup/2");
- ArrayMap backupSecondaryPackages = new ArrayMap<>();
+ ArrayMap<Integer, String> backupSecondaryPackages = new ArrayMap<>();
backupSecondaryPackages.put(0, "");
backupSecondaryPackages.put(10,
"this.is.another.package.backup:package.backup");
@@ -368,7 +380,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
serializer.endDocument();
serializer.flush();
- for (int userId : mUserProfiles.getCurrentProfileIds()) {
+ for (int userId : mUserProfiles.getCurrentProfileIds().toArray()) {
List<String> expected =
stringToList(mExpectedPrimary.get(approvalLevel).get(userId));
List<String> actual = stringToList(Settings.Secure.getStringForUser(
@@ -633,7 +645,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- public void testGetAllowedComponents() throws Exception {
+ public void testGetAllowedComponentsByUser() throws Exception {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
APPROVAL_BY_COMPONENT);
loadXml(service);
@@ -708,6 +720,145 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertTrue(services.isSameUser(service, 10));
}
+ @Test
+ public void testGetAllowedComponents() throws Exception {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ loadXml(service);
+
+ SparseArray<ArraySet<ComponentName>> expected = new SparseArray<>();
+
+ ArraySet<ComponentName> expected10 = new ArraySet<>();
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/M1"));
+ expected10.add(ComponentName.unflattenFromString("this.is.another.package/with.Component"));
+ expected10.add(ComponentName.unflattenFromString("component/2"));
+ expected10.add(ComponentName.unflattenFromString("package/component2"));
+ expected.put(10, expected10);
+ ArraySet<ComponentName> expected0 = new ArraySet<>();
+ expected0.add(ComponentName.unflattenFromString("secondary/component.Name"));
+ expected0.add(ComponentName.unflattenFromString("this.is.a.package.name/Ba"));
+ expected0.add(ComponentName.unflattenFromString("another.package/B1"));
+ expected.put(0, expected0);
+ ArraySet<ComponentName> expected12 = new ArraySet<>();
+ expected12.add(ComponentName.unflattenFromString("bananas!/Bananas!"));
+ expected.put(12, expected12);
+ expected.put(11, new ArraySet<>());
+
+ SparseArray<ArraySet<ComponentName>> actual =
+ service.getAllowedComponents(mUserProfiles.getCurrentProfileIds());
+
+ assertContentsInAnyOrder(expected, actual);
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind_forceRebind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(true, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(2, componentsToUnbind.size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertTrue(componentsToUnbind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ }
+
+ @Test
+ public void testPopulateComponentsToUnbind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ IInterface iInterface = mock(IInterface.class);
+ when(iInterface.asBinder()).thenReturn(mock(IBinder.class));
+
+ ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
+ mock(ServiceConnection.class), 26);
+ ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
+ iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
+ mock(ServiceConnection.class), 26);
+ Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
+ removableBoundServices.add(service0);
+ removableBoundServices.add(service0a);
+ removableBoundServices.add(service10);
+
+ SparseArray<Set<ComponentName>> allowedComponentsToBind = new SparseArray<>();
+ Set<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ allowedComponentsToBind.put(0, allowed0);
+ Set<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowedComponentsToBind.put(10, allowed10);
+
+ SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ service.populateComponentsToUnbind(false, removableBoundServices, allowedComponentsToBind,
+ componentsToUnbind);
+
+ assertEquals(1, componentsToUnbind.size());
+ assertEquals(1, componentsToUnbind.get(0).size());
+ assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
+ @Test
+ public void populateComponentsToBind() {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+
+ SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+ ArraySet<ComponentName> allowed0 = new ArraySet<>();
+ allowed0.add(ComponentName.unflattenFromString("a/a"));
+ approvedComponentsByUser.put(0, allowed0);
+ ArraySet<ComponentName> allowed10 = new ArraySet<>();
+ allowed10.add(ComponentName.unflattenFromString("b/b"));
+ allowed10.add(ComponentName.unflattenFromString("c/c"));
+ approvedComponentsByUser.put(10, allowed10);
+ ArraySet<ComponentName> allowed15 = new ArraySet<>();
+ allowed15.add(ComponentName.unflattenFromString("d/d"));
+ approvedComponentsByUser.put(15, allowed15);
+
+ IntArray users = new IntArray();
+ users.add(10);
+ users.add(0);
+
+ SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+ service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+
+ assertEquals(2, componentsToBind.size());
+ assertEquals(1, componentsToBind.get(0).size());
+ assertTrue(componentsToBind.get(0).contains(ComponentName.unflattenFromString("a/a")));
+ assertEquals(2, componentsToBind.get(10).size());
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("b/b")));
+ assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
+ }
+
private void loadXml(ManagedServices service) throws Exception {
final StringBuffer xml = new StringBuffer();
xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -775,7 +926,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
ManagedServices.ENABLED_SERVICES_SEPARATOR)));
}
- private void assertContentsInAnyOrder(List<?> expected, List<?> actual) {
+ private void assertContentsInAnyOrder(Collection<?> expected, Collection<?> actual) {
+ assertNotNull(actual);
assertEquals(expected.size(), actual.size());
for (Object o : expected) {
@@ -787,6 +939,21 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
}
+ private void assertContentsInAnyOrder(SparseArray<ArraySet<ComponentName>> expected,
+ SparseArray<ArraySet<ComponentName>> actual) throws Exception {
+ assertEquals(expected.size(), actual.size());
+
+ for (int i = 0; i < expected.size(); i++) {
+ int key = expected.keyAt(i);
+ assertTrue(actual.indexOfKey(key) >= 0);
+ try {
+ assertContentsInAnyOrder(expected.valueAt(i), actual.get(key));
+ } catch (Throwable t) {
+ throw new Exception("Error validating " + key, t);
+ }
+ }
+ }
+
private void verifyExpectedBoundEntries(ManagedServices service, boolean primary)
throws Exception {
ArrayMap<Integer, String> verifyMap = primary ? mExpectedPrimary.get(service.mApprovalLevel)
@@ -920,7 +1087,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
@Override
protected boolean checkType(IInterface service) {
- return false;
+ return true;
}
@Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f9a4f784bf4f..1de1e4ef8b9f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -33,6 +33,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.UserManager;
+import android.util.IntArray;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
@@ -103,7 +104,12 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
}
when(mUm.getUsers()).thenReturn(users);
when(mUm.getUsers(anyBoolean())).thenReturn(users);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
+ IntArray profileIds = new IntArray();
+ profileIds.add(0);
+ profileIds.add(11);
+ profileIds.add(10);
+ profileIds.add(12);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 88c6fcf138cf..b955e56c80a8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.IntArray;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -235,18 +236,22 @@ public class SnoozeHelperTest extends UiServiceTestCase {
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
mSnoozeHelper.snooze(r4, 1000);
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(3, mSnoozeHelper.getSnoozed().size());
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_CURRENT});
+ profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
assertEquals(1, mSnoozeHelper.getSnoozed().size());
}
@Test
public void testGetSnoozedByUser_managedProfiles() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM, UserHandle.USER_CURRENT});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
@@ -273,8 +278,9 @@ public class SnoozeHelperTest extends UiServiceTestCase {
@Test
public void repostGroupSummary_repostsSummary() throws Exception {
- when(mUserProfiles.getCurrentProfileIds()).thenReturn(
- new int[] {UserHandle.USER_SYSTEM});
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_SYSTEM);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
NotificationRecord r = getNotificationRecord(
"pkg", 1, "one", UserHandle.SYSTEM, "group1", true);
NotificationRecord r2 = getNotificationRecord(