summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Wren <cwren@android.com> 2016-01-08 15:50:13 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-01-08 15:50:13 +0000
commit2d20a4dcc60a4d87ddf536d40cf00a59fa2d9479 (patch)
treeeb3886f1bd5826ac8f3920bc91263e8a318ca5a6
parentc03c596a537df81bcb0ab4629e56c2ce1be4c0ed (diff)
parentab41eecf22352f54167ce9a272a397715ffd0015 (diff)
Merge "allow listeners to disable themselves"
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--api/test-current.txt2
-rw-r--r--core/java/android/app/INotificationManager.aidl3
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java5
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java30
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java65
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java32
8 files changed, 138 insertions, 3 deletions
diff --git a/api/current.txt b/api/current.txt
index 6bcf19fc28d8..f6b4d6105c00 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33591,6 +33591,8 @@ package android.service.notification {
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
+ method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+ method public final void requestUnbind() throws android.os.RemoteException;
method public final void setNotificationsShown(java.lang.String[]);
field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 9a302c5260e9..bf4fc5004b16 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -35730,6 +35730,8 @@ package android.service.notification {
method public void registerAsSystemService(android.content.Context, android.content.ComponentName, int) throws android.os.RemoteException;
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
+ method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+ method public final void requestUnbind() throws android.os.RemoteException;
method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public void unregisterAsSystemService() throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 4777ba86ccff..8ff46c2ef267 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -33605,6 +33605,8 @@ package android.service.notification {
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
+ method public static final void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
+ method public final void requestUnbind() throws android.os.RemoteException;
method public final void setNotificationsShown(java.lang.String[]);
field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e60cb0377e96..633f6995b986 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -68,6 +68,9 @@ interface INotificationManager
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
+ void requestBindListener(in ComponentName component);
+ void requestUnbindListener(in INotificationListener token);
+
void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 3a8956ec15d9..aba82fad0bfa 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -17,13 +17,18 @@
package android.service.notification;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.INotificationManager;
import android.app.Notification;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
/**
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1fd5f95ae9bf..ed90e795af00 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -692,6 +692,36 @@ public abstract class NotificationListenerService extends Service {
}
}
+ /**
+ * Request that the listener be rebound, after a previous call to (@link requestUnbind).
+ *
+ * <P>This method will fail for assistants that have
+ * not been granted the permission by the user.
+ *
+ * <P>The service should wait for the {@link #onListenerConnected()} event
+ * before performing any operations.
+ */
+ public static final void requestRebind(ComponentName componentName)
+ throws RemoteException {
+ INotificationManager noMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ noMan.requestBindListener(componentName);
+ }
+
+ /**
+ * Request that the service be unbound.
+ *
+ * <P>This will no longer receive updates until
+ * {@link #requestRebind(ComponentName)} is called.
+ * The service will likely be kiled by the system after this call.
+ */
+ public final void requestUnbind() throws RemoteException {
+ if (mWrapper != null) {
+ INotificationManager noMan = getNotificationInterface();
+ noMan.requestUnbindListener(mWrapper);
+ }
+ }
+
/** Convert new-style Icons to legacy representations for pre-M clients. */
private void createLegacyIconExtras(Notification n) {
Icon smallIcon = n.getSmallIcon();
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 09e66475c1be..f360dc2c4283 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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
+ (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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
private void rebuildRestoredPackages() {
mRestoredPackages.clear();
+ mSnoozingForCurrentProfiles.clear();
String settingName = restoredSettingName(mConfig);
int[] userIds = mUserProfiles.getCurrentProfileIds();
final int N = userIds.length;
@@ -525,6 +574,7 @@ abstract public class ManagedServices {
add.removeAll(c);
}
}
+ add.removeAll(mSnoozingForCurrentProfiles);
toAdd.put(userIds[i], add);
@@ -803,6 +853,10 @@ abstract public class ManagedServices {
return ManagedServices.this != host;
}
+ public ManagedServices getOwner() {
+ return ManagedServices.this;
+ }
+
@Override
public String toString() {
return new StringBuilder("ManagedServiceInfo[")
@@ -846,6 +900,11 @@ abstract public class ManagedServices {
}
}
+ /** 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 6b0399bd31a1..e787eda171ea 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1446,6 +1446,37 @@ public class NotificationManagerService extends SystemService {
}
}
+ /**
+ * 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();
@@ -3426,6 +3457,7 @@ public class NotificationManagerService extends SystemService {
}
}
+
@Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (mListenersDisablingEffects.remove(removed)) {