allow listeners to disable themselves
This decouples the user permisson grant from the
bound state of the listener. This allows listeners
that are only relevant sometimes to avoid being
bound as a foreground service all the time.
Bug: 19232554
Change-Id: I9ab078630af5a10c6878d3f19ba80661299713d5
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 09e6647..f360dc2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -86,7 +86,7 @@
protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
// things that will be put into mServices as soon as they're ready
private final ArrayList<String> mServicesBinding = new ArrayList<String>();
- // lists the component names of all enabled (and therefore connected)
+ // lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
= new ArraySet<ComponentName>();
@@ -97,6 +97,8 @@
private ArraySet<String> mRestoredPackages = new ArraySet<>();
// State of current service categories
private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>();
+ // List of enabled packages that have nevertheless asked not to be run
+ private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
// Kept to de-dupe user change events (experienced after boot, when we receive a settings and a
@@ -174,6 +176,12 @@
+ (info.isSystem?" SYSTEM":"")
+ (info.isGuest(this)?" GUEST":""));
}
+
+ pw.println(" Snoozed " + getCaption() + "s (" +
+ mSnoozingForCurrentProfiles.size() + "):");
+ for (ComponentName name : mSnoozingForCurrentProfiles) {
+ pw.println(" " + name.flattenToShortString());
+ }
}
// By convention, restored settings are replicated to another settings
@@ -242,14 +250,25 @@
rebindServices();
}
- public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
- checkNotNull(service);
+ public ManagedServiceInfo getServiceFromTokenLocked(IInterface service) {
+ if (service == null) {
+ return null;
+ }
final IBinder token = service.asBinder();
final int N = mServices.size();
for (int i = 0; i < N; i++) {
final ManagedServiceInfo info = mServices.get(i);
if (info.service.asBinder() == token) return info;
}
+ return null;
+ }
+
+ public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
+ checkNotNull(service);
+ ManagedServiceInfo info = getServiceFromTokenLocked(service);
+ if (info != null) {
+ return info;
+ }
throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
+ service);
}
@@ -278,6 +297,35 @@
checkType(guest.service);
if (registerServiceImpl(guest) != null) {
onServiceAdded(guest);
+ onServiceAdded(guest);
+ }
+ }
+
+ public void setComponentState(ComponentName component, boolean enabled) {
+ boolean previous = !mSnoozingForCurrentProfiles.contains(component);
+ if (previous == enabled) {
+ return;
+ }
+
+ if (enabled) {
+ mSnoozingForCurrentProfiles.remove(component);
+ } else {
+ mSnoozingForCurrentProfiles.add(component);
+ }
+
+ // State changed
+ if (DEBUG) {
+ Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " +
+ component.flattenToShortString());
+ }
+
+ final int[] userIds = mUserProfiles.getCurrentProfileIds();
+ for (int userId : userIds) {
+ if (enabled) {
+ registerServiceLocked(component, userId);
+ } else {
+ unregisterServiceLocked(component, userId);
+ }
}
}
@@ -324,6 +372,7 @@
private void rebuildRestoredPackages() {
mRestoredPackages.clear();
+ mSnoozingForCurrentProfiles.clear();
String settingName = restoredSettingName(mConfig);
int[] userIds = mUserProfiles.getCurrentProfileIds();
final int N = userIds.length;
@@ -525,6 +574,7 @@
add.removeAll(c);
}
}
+ add.removeAll(mSnoozingForCurrentProfiles);
toAdd.put(userIds[i], add);
@@ -803,6 +853,10 @@
return ManagedServices.this != host;
}
+ public ManagedServices getOwner() {
+ return ManagedServices.this;
+ }
+
@Override
public String toString() {
return new StringBuilder("ManagedServiceInfo[")
@@ -846,6 +900,11 @@
}
}
+ /** convenience method for looking in mEnabledServicesForCurrentProfiles */
+ public boolean isComponentEnabledForCurrentProfiles(ComponentName component) {
+ return mEnabledServicesForCurrentProfiles.contains(component);
+ }
+
public static class UserProfiles {
// Profiles of the current user.
private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 82c38af..1c49440 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1445,6 +1445,37 @@
}
}
+ /**
+ * Handle request from an approved listener to re-enable itself.
+ *
+ * @param component The componenet to be re-enabled, caller must match package.
+ */
+ @Override
+ public void requestBindListener(ComponentName component) {
+ checkCallerIsSystemOrSameApp(component.getPackageName());
+ long identity = Binder.clearCallingIdentity();
+ try {
+ ManagedServices manager = mAssistant.isComponentEnabledForCurrentProfiles(component)
+ ? mAssistant
+ : mListeners;
+ manager.setComponentState(component, true);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void requestUnbindListener(INotificationListener token) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // allow bound services to disable themselves
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ info.getOwner().setComponentState(info.component, false);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
long identity = Binder.clearCallingIdentity();
@@ -3432,6 +3463,7 @@
}
}
+
@Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (mListenersDisablingEffects.remove(removed)) {