implement assistant service connection
Added a guest-mode to ManagedServices.
Like system services, the lifecycle of a guest is not managed.
Unlike system services, guests are not considered privledged.
The Assistant gets all the usual listener events.
Implemented adjustImportance.
Future work: enqueued, clicked, visibility, removed, annotations
Bug: 22455414
Change-Id: Ic41c0bf625b5e98cb577b49098bba23a539bb507
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 1987214..ce18818 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -121,6 +121,11 @@
}
@Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof IConditionProvider;
+ }
+
+ @Override
public void onBootPhaseAppsCanStart() {
super.onBootPhaseAppsCanStart();
for (int i = 0; i < mSystemConditionProviders.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7662da..09e6647 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -141,6 +141,8 @@
abstract protected IInterface asInterface(IBinder binder);
+ abstract protected boolean checkType(IInterface service);
+
abstract protected void onServiceAdded(ManagedServiceInfo info);
protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
@@ -169,7 +171,8 @@
if (filter != null && !filter.matches(info.component)) continue;
pw.println(" " + info.component
+ " (user " + info.userid + "): " + info.service
- + (info.isSystem?" SYSTEM":""));
+ + (info.isSystem?" SYSTEM":"")
+ + (info.isGuest(this)?" GUEST":""));
}
}
@@ -266,6 +269,18 @@
}
}
+ /**
+ * Add a service to our callbacks. The lifecycle of this service is managed externally,
+ * but unlike a system service, it should not be considered privledged.
+ * */
+ public void registerGuestService(ManagedServiceInfo guest) {
+ checkNotNull(guest.service);
+ checkType(guest.service);
+ if (registerServiceImpl(guest) != null) {
+ onServiceAdded(guest);
+ }
+ }
+
public void setCategoryState(String category, boolean enabled) {
synchronized (mMutex) {
final Boolean previous = mCategoryEnabled.put(category, enabled);
@@ -484,7 +499,7 @@
synchronized (mMutex) {
// Unbind automatically bound services, retain system services.
for (ManagedServiceInfo service : mServices) {
- if (!service.isSystem) {
+ if (!service.isSystem && !service.isGuest(this)) {
toRemove.add(service);
}
}
@@ -709,11 +724,15 @@
private ManagedServiceInfo registerServiceImpl(final IInterface service,
final ComponentName component, final int userid) {
+ ManagedServiceInfo info = newServiceInfo(service, component, userid,
+ true /*isSystem*/, null /*connection*/, Build.VERSION_CODES.LOLLIPOP);
+ return registerServiceImpl(info);
+ }
+
+ private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
synchronized (mMutex) {
try {
- ManagedServiceInfo info = newServiceInfo(service, component, userid,
- true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
- service.asBinder().linkToDeath(info, 0);
+ info.service.asBinder().linkToDeath(info, 0);
mServices.add(info);
return info;
} catch (RemoteException e) {
@@ -728,7 +747,7 @@
*/
private void unregisterServiceImpl(IInterface service, int userid) {
ManagedServiceInfo info = removeServiceImpl(service, userid);
- if (info != null && info.connection != null) {
+ if (info != null && info.connection != null && !info.isGuest(this)) {
mContext.unbindService(info.connection);
}
}
@@ -780,6 +799,10 @@
this.targetSdkVersion = targetSdkVersion;
}
+ public boolean isGuest(ManagedServices host) {
+ return ManagedServices.this != host;
+ }
+
@Override
public String toString() {
return new StringBuilder("ManagedServiceInfo[")
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 49aa73e..82c38af 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -39,6 +39,7 @@
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -96,6 +97,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
@@ -154,6 +156,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -167,11 +170,13 @@
// message codes
static final int MESSAGE_TIMEOUT = 2;
static final int MESSAGE_SAVE_POLICY_FILE = 3;
- static final int MESSAGE_RECONSIDER_RANKING = 4;
- static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
- static final int MESSAGE_SEND_RANKING_UPDATE = 6;
- static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
- static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
+ static final int MESSAGE_SEND_RANKING_UPDATE = 4;
+ static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
+ static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
+
+ // ranking thread messages
+ private static final int MESSAGE_RECONSIDER_RANKING = 1000;
+ private static final int MESSAGE_RANKING_SORT = 1001;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -281,11 +286,13 @@
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
+ private NotificationAssistant mAssistant;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
+ private RankingHandler mRankingHandler;
private static class Archive {
final int mBufferSize;
@@ -738,6 +745,7 @@
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
+ mAssistant.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
@@ -779,6 +787,7 @@
// Refresh managed services
mConditionProviders.onUserSwitched(user);
mListeners.onUserSwitched(user);
+ mAssistant.onUserSwitched(user);
mZenModeHelper.onUserSwitched(user);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
mUserProfiles.updateCache(context);
@@ -874,8 +883,9 @@
extractorNames = new String[0];
}
mUsageStats = new NotificationUsageStats(getContext());
+ mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
- new RankingWorkerHandler(mRankingThread.getLooper()),
+ mRankingHandler,
mUsageStats,
extractorNames);
mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
@@ -909,6 +919,7 @@
importOldBlockDb();
mListeners = new NotificationListeners();
+ mAssistant = new NotificationAssistant();
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -1025,6 +1036,7 @@
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
+ mAssistant.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1896,6 +1908,22 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void setImportanceFromAssistant(INotificationListener token, String key,
+ int importance, CharSequence explanation) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ mAssistant.checkServiceTokenLocked(token);
+ NotificationRecord n = mNotificationsByKey.get(key);
+ n.setImportance(importance, explanation);
+ mRankingHandler.requestSort();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
};
private String disableNotificationEffects(NotificationRecord record) {
@@ -2037,6 +2065,8 @@
pw.print(listener.component);
}
pw.println(')');
+ pw.println("\n Notification assistant:");
+ mAssistant.dump(pw, filter);
}
pw.println("\n Policy access:");
pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2686,7 +2716,7 @@
}
}
- private void handleRankingConfigChange() {
+ private void handleRankingSort() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
@@ -2788,9 +2818,9 @@
}
- private final class RankingWorkerHandler extends Handler
+ private final class RankingHandlerWorker extends Handler implements RankingHandler
{
- public RankingWorkerHandler(Looper looper) {
+ public RankingHandlerWorker(Looper looper) {
super(looper);
}
@@ -2800,11 +2830,23 @@
case MESSAGE_RECONSIDER_RANKING:
handleRankingReconsideration(msg);
break;
- case MESSAGE_RANKING_CONFIG_CHANGE:
- handleRankingConfigChange();
+ case MESSAGE_RANKING_SORT:
+ handleRankingSort();
break;
}
}
+
+ public void requestSort() {
+ removeMessages(MESSAGE_RANKING_SORT);
+ sendEmptyMessage(MESSAGE_RANKING_SORT);
+ }
+
+ public void requestReconsideration(RankingReconsideration recon) {
+ Message m = Message.obtain(this,
+ NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
+ long delay = recon.getDelay(TimeUnit.MILLISECONDS);
+ sendMessageDelayed(m, delay);
+ }
}
// Notifications
@@ -3305,6 +3347,45 @@
return true;
}
+ public class NotificationAssistant extends ManagedServices {
+
+ public NotificationAssistant() {
+ super(getContext(), mHandler, mNotificationList, mUserProfiles);
+ }
+
+ @Override
+ protected Config getConfig() {
+ Config c = new Config();
+ c.caption = "notification assistant";
+ c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
+ c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
+ c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+ c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
+ c.clientLabel = R.string.notification_assistant_binding_label;
+ return c;
+ }
+
+ @Override
+ protected IInterface asInterface(IBinder binder) {
+ return INotificationListener.Stub.asInterface(binder);
+ }
+
+ @Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof INotificationListener;
+ }
+
+ @Override
+ protected void onServiceAdded(ManagedServiceInfo info) {
+ mListeners.registerGuestService(info);
+ }
+
+ @Override
+ protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+ mListeners.unregisterService(removed.service, removed.userid);
+ }
+ }
+
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
@@ -3332,6 +3413,11 @@
}
@Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof INotificationListener;
+ }
+
+ @Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
@@ -3366,7 +3452,6 @@
public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
-
}
/**
diff --git a/services/core/java/com/android/server/notification/RankingHandler.java b/services/core/java/com/android/server/notification/RankingHandler.java
new file mode 100644
index 0000000..80bb4f0
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+public interface RankingHandler {
+ public void requestSort();
+ public void requestReconsideration(RankingReconsideration recon);
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3287f67..0662e7a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -19,10 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
-import android.os.Message;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -40,7 +37,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
public class RankingHelper implements RankingConfig {
private static final String TAG = "RankingHelper";
@@ -73,10 +69,10 @@
private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
private final Context mContext;
- private final Handler mRankingHandler;
+ private final RankingHandler mRankingHandler;
- public RankingHelper(Context context, Handler rankingHandler, NotificationUsageStats usageStats,
- String[] extractorNames) {
+ public RankingHelper(Context context, RankingHandler rankingHandler,
+ NotificationUsageStats usageStats, String[] extractorNames) {
mContext = context;
mRankingHandler = rankingHandler;
@@ -119,10 +115,7 @@
try {
RankingReconsideration recon = extractor.process(r);
if (recon != null) {
- Message m = Message.obtain(mRankingHandler,
- NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
- long delay = recon.getDelay(TimeUnit.MILLISECONDS);
- mRankingHandler.sendMessageDelayed(m, delay);
+ mRankingHandler.requestReconsideration(recon);
}
} catch (Throwable t) {
Slog.w(TAG, "NotificationSignalExtractor failed.", t);
@@ -287,7 +280,7 @@
for (int i = 0; i < N; i++) {
mSignalExtractors[i].setConfig(this);
}
- mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
+ mRankingHandler.requestSort();
}
public void sort(ArrayList<NotificationRecord> notificationList) {
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index df7b412..2640889 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -35,7 +35,7 @@
public class RankingHelperTest extends AndroidTestCase {
@Mock NotificationUsageStats mUsageStats;
- @Mock Handler handler;
+ @Mock RankingHandler handler;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;