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
This commit is contained in:
Chris Wren
2015-12-15 15:34:46 -05:00
parent 4a5b54fb3b
commit 51017d0e23
16 changed files with 320 additions and 167 deletions

View File

@@ -226,7 +226,6 @@ LOCAL_SRC_FILES += \
core/java/android/service/carrier/ICarrierMessagingService.aidl \
core/java/android/service/gatekeeper/IGateKeeperService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
core/java/android/service/notification/INotificationAssistant.aidl \
core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
core/java/android/service/notification/IConditionListener.aidl \
core/java/android/service/notification/IConditionProvider.aidl \

View File

@@ -33491,20 +33491,13 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
public class NotificationAdjustment implements android.os.Parcelable {
ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
}
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33524,6 +33517,10 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public class NotificationAssistantService.Adjustment {
ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
}
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();

View File

@@ -35633,20 +35633,13 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
public class NotificationAdjustment implements android.os.Parcelable {
ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
}
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -35666,6 +35659,10 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public class NotificationAssistantService.Adjustment {
ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
}
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();

View File

@@ -33494,20 +33494,13 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
public class NotificationAdjustment implements android.os.Parcelable {
ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
}
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33527,6 +33520,10 @@ package android.service.notification {
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
public class NotificationAssistantService.Adjustment {
ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
}
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();

View File

@@ -78,6 +78,8 @@ interface INotificationManager
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter);
void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
boolean isSystemConditionProviderEnabled(String path);

View File

@@ -1,37 +0,0 @@
/**
* 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 android.service.notification;
import android.service.notification.NotificationAdjustment;
import android.service.notification.IStatusBarNotificationHolder;
import android.service.notification.NotificationRankingUpdate;
/** @hide */
interface INotificationAssistant
{
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
void onNotificationVisibilityChanged(String key, long time, boolean visible);
void onNotificationClick(String key, long time);
void onNotificationActionClick(String key, long time, int actionIndex);
void onNotificationRemoved(String key, long time, int reason);
}

View File

@@ -23,6 +23,7 @@ import android.service.notification.NotificationRankingUpdate;
/** @hide */
oneway interface INotificationListener
{
// listeners and assistants
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
@@ -31,4 +32,11 @@ oneway interface INotificationListener
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
// assistants only
void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
void onNotificationVisibilityChanged(String key, long time, boolean visible);
void onNotificationClick(String key, long time);
void onNotificationActionClick(String key, long time, int actionIndex);
void onNotificationRemovedReason(String key, long time, int reason);
}

View File

@@ -1,57 +0,0 @@
package android.service.notification;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
public class NotificationAdjustment implements Parcelable {
int mImportance;
CharSequence mExplanation;
Uri mReference;
/**
* Create a notification importance adjustment.
*
* @param importance The final importance of the notification.
* @param explanation A human-readable justification for the adjustment.
* @param reference A reference to an external object that augments the
* explanation, such as a
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
* or null.
*/
public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) {
mImportance = importance;
mExplanation = explanation;
mReference = reference;
}
private NotificationAdjustment(Parcel source) {
this(source.readInt(), source.readCharSequence(),
(Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader()));
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mImportance);
dest.writeCharSequence(mExplanation);
dest.writeParcelable(mReference, 0);
}
public static final Parcelable.Creator<NotificationAdjustment> CREATOR
= new Parcelable.Creator<NotificationAdjustment>() {
@Override
public NotificationAdjustment createFromParcel(Parcel source) {
return new NotificationAdjustment(source);
}
@Override
public NotificationAdjustment[] newArray(int size) {
return new NotificationAdjustment[size];
}
};
}

View File

@@ -20,8 +20,11 @@ import android.annotation.SdkConstant;
import android.app.Notification;
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.util.Log;
/**
* A service that helps the user manage notifications by modifying the
@@ -39,6 +42,8 @@ import android.os.Parcelable;
* &lt;/service></pre>
*/
public abstract class NotificationAssistantService extends NotificationListenerService {
private static final String TAG = "NotificationAssistant";
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -85,6 +90,36 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/** Notification was canceled because it was an invisible member of a group. */
public static final int REASON_GROUP_OPTIMIZATION = 13;
public class Adjustment {
int mImportance;
CharSequence mExplanation;
Uri mReference;
/**
* Create a notification importance adjustment.
*
* @param importance The final importance of the notification.
* @param explanation A human-readable justification for the adjustment.
* @param reference A reference to an external object that augments the
* explanation, such as a
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
* or null.
*/
public Adjustment(int importance, CharSequence explanation, Uri reference) {
mImportance = importance;
mExplanation = explanation;
mReference = reference;
}
}
@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new NotificationAssistantWrapper();
}
return mWrapper;
}
/**
* A notification was posted by an app. Called before alert.
*
@@ -93,7 +128,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* @param user true if the initial importance reflects an explicit user preference.
* @return an adjustment or null to take no action, within 100ms.
*/
abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn,
abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
int importance, boolean user);
/**
@@ -150,9 +185,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* @param key the notification key
* @param adjustment the new importance with an explanation
*/
public final void adjustImportance(String key, NotificationAdjustment adjustment)
public final void adjustImportance(String key, Adjustment adjustment)
{
// TODO: pack up the adjustment and send it to the NotificationManager.
if (!isBound()) return;
try {
getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
adjustment.mImportance, adjustment.mExplanation);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
/**
@@ -160,7 +201,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* be fired when the host notification is deleted, or when this annotation
* is removed or replaced.
*
* @param key the notification key
* @param key the key of the notification to be annotated
* @param annotation the new annotation object
*/
public final void setAnnotation(String key, Notification annotation)
@@ -171,10 +212,74 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/**
* Remove the annotation from a notification.
*
* @param key the notification key
* @param key the key of the notification to be cleansed of annotatons
*/
public final void clearAnnotation(String key)
{
// TODO: ask the NotificationManager to clear the annotation.
}
private class NotificationAssistantWrapper extends NotificationListenerWrapper {
@Override
public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
int importance, boolean user) throws RemoteException {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
} catch (RemoteException e) {
Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
return;
}
try {
Adjustment adjustment =
NotificationAssistantService.this.onNotificationEnqueued(sbn, importance, user);
if (adjustment != null) {
adjustImportance(sbn.getKey(), adjustment);
}
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationEnqueued", t);
}
}
@Override
public void onNotificationVisibilityChanged(String key, long time, boolean visible)
throws RemoteException {
try {
NotificationAssistantService.this.onNotificationVisibilityChanged(key, time,
visible);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationVisibilityChanged", t);
}
}
@Override
public void onNotificationClick(String key, long time) throws RemoteException {
try {
NotificationAssistantService.this.onNotificationClick(key, time);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationClick", t);
}
}
@Override
public void onNotificationActionClick(String key, long time, int actionIndex)
throws RemoteException {
try {
NotificationAssistantService.this.onNotificationActionClick(key, time, actionIndex);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationActionClick", t);
}
}
@Override
public void onNotificationRemovedReason(String key, long time, int reason)
throws RemoteException {
try {
NotificationAssistantService.this.onNotificationRemoved(key, time, reason);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package android.service.notification;
import android.service.notification.IStatusBarNotificationHolder;
import android.annotation.SystemApi;
import android.annotation.SdkConstant;
@@ -151,7 +152,8 @@ public abstract class NotificationListenerService extends Service {
@SystemApi
public static final int TRIM_LIGHT = 1;
private INotificationListenerWrapper mWrapper = null;
/** @hide */
protected NotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
private INotificationManager mNoMan;
@@ -291,7 +293,8 @@ public abstract class NotificationListenerService extends Service {
// optional
}
private final INotificationManager getNotificationInterface() {
/** @hide */
protected final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -634,12 +637,13 @@ public abstract class NotificationListenerService extends Service {
@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
mWrapper = new NotificationListenerWrapper();
}
return mWrapper;
}
private boolean isBound() {
/** @hide */
protected boolean isBound() {
if (mWrapper == null) {
Log.w(TAG, "Notification listener service not yet bound.");
return false;
@@ -664,7 +668,7 @@ public abstract class NotificationListenerService extends Service {
int currentUser) throws RemoteException {
mSystemContext = context;
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
mWrapper = new NotificationListenerWrapper();
}
INotificationManager noMan = getNotificationInterface();
noMan.registerListener(mWrapper, componentName, currentUser);
@@ -716,7 +720,8 @@ public abstract class NotificationListenerService extends Service {
}
}
private class INotificationListenerWrapper extends INotificationListener.Stub {
/** @hide */
protected class NotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
NotificationRankingUpdate update) {
@@ -817,6 +822,35 @@ public abstract class NotificationListenerService extends Service {
Log.w(TAG, "Error running onInterruptionFilterChanged", t);
}
}
@Override
public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder,
int importance, boolean user) throws RemoteException {
// no-op in the listener
}
@Override
public void onNotificationVisibilityChanged(String key, long time, boolean visible)
throws RemoteException {
// no-op in the listener
}
@Override
public void onNotificationClick(String key, long time) throws RemoteException {
// no-op in the listener
}
@Override
public void onNotificationActionClick(String key, long time, int actionIndex)
throws RemoteException {
// no-op in the listener
}
@Override
public void onNotificationRemovedReason(String key, long time, int reason)
throws RemoteException {
// no-op in the listener
}
}
private void applyUpdate(NotificationRankingUpdate update) {

View File

@@ -120,6 +120,11 @@ public class ConditionProviders extends ManagedServices {
return IConditionProvider.Stub.asInterface(binder);
}
@Override
protected boolean checkType(IInterface service) {
return service instanceof IConditionProvider;
}
@Override
public void onBootPhaseAppsCanStart() {
super.onBootPhaseAppsCanStart();

View File

@@ -141,6 +141,8 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
}
}
/**
* 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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
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 @@ abstract public class ManagedServices {
*/
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 @@ abstract public class ManagedServices {
this.targetSdkVersion = targetSdkVersion;
}
public boolean isGuest(ManagedServices host) {
return ManagedServices.this != host;
}
@Override
public String toString() {
return new StringBuilder("ManagedServiceInfo[")

View File

@@ -39,6 +39,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
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.Condition;
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.Iterator;
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 @@ public class NotificationManagerService extends SystemService {
// 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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
mAssistant.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
@@ -779,6 +787,7 @@ public class NotificationManagerService extends SystemService {
// 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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
importOldBlockDb();
mListeners = new NotificationListeners();
mAssistant = new NotificationAssistant();
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -1025,6 +1036,7 @@ public class NotificationManagerService extends SystemService {
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
mAssistant.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1896,6 +1908,22 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
}
}
private void handleRankingConfigChange() {
private void handleRankingSort() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
@@ -2788,9 +2818,9 @@ public class NotificationManagerService extends SystemService {
}
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 @@ public class NotificationManagerService extends SystemService {
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 @@ public class NotificationManagerService extends SystemService {
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<>();
@@ -3331,6 +3412,11 @@ public class NotificationManagerService extends SystemService {
return INotificationListener.Stub.asInterface(binder);
}
@Override
protected boolean checkType(IInterface service) {
return service instanceof INotificationListener;
}
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
@@ -3366,7 +3452,6 @@ public class NotificationManagerService extends SystemService {
public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
}
/**

View File

@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
package android.service.notification;
parcelable NotificationAdjustment;
public interface RankingHandler {
public void requestSort();
public void requestReconsideration(RankingReconsideration recon);
}

View File

@@ -19,10 +19,7 @@ import android.app.Notification;
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.ArrayList;
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 @@ public class RankingHelper implements RankingConfig {
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 @@ public class RankingHelper implements RankingConfig {
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 @@ public class RankingHelper implements RankingConfig {
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) {

View File

@@ -35,7 +35,7 @@ import java.util.ArrayList;
public class RankingHelperTest extends AndroidTestCase {
@Mock NotificationUsageStats mUsageStats;
@Mock Handler handler;
@Mock RankingHandler handler;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;