Allow companion device mgrs channel access
- Callbacks when channels and groups are modified - Allow them to read and update channels and groups Test: runtest systemui-notification Change-Id: Ie4d02bd4583f71f9faf27603bcc59a1ec0eeaf46
This commit is contained in:
@@ -37306,12 +37306,16 @@ package android.service.notification {
|
||||
method public final int getCurrentInterruptionFilter();
|
||||
method public final int getCurrentListenerHints();
|
||||
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
|
||||
method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
|
||||
method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
|
||||
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
|
||||
method public android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onInterruptionFilterChanged(int);
|
||||
method public void onListenerConnected();
|
||||
method public void onListenerDisconnected();
|
||||
method public void onListenerHintsChanged(int);
|
||||
method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
|
||||
method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
|
||||
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
|
||||
@@ -37324,6 +37328,7 @@ package android.service.notification {
|
||||
method public final void requestUnbind();
|
||||
method public final void setNotificationsShown(java.lang.String[]);
|
||||
method public final void snoozeNotification(java.lang.String, long);
|
||||
method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
|
||||
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
|
||||
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
|
||||
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
|
||||
@@ -37332,6 +37337,9 @@ package android.service.notification {
|
||||
field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
|
||||
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
|
||||
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
|
||||
field public static final int REASON_APP_CANCEL = 8; // 0x8
|
||||
field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
|
||||
field public static final int REASON_CANCEL = 2; // 0x2
|
||||
|
||||
@@ -40430,12 +40430,16 @@ package android.service.notification {
|
||||
method public final int getCurrentInterruptionFilter();
|
||||
method public final int getCurrentListenerHints();
|
||||
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
|
||||
method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
|
||||
method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
|
||||
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
|
||||
method public android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onInterruptionFilterChanged(int);
|
||||
method public void onListenerConnected();
|
||||
method public void onListenerDisconnected();
|
||||
method public void onListenerHintsChanged(int);
|
||||
method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
|
||||
method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
|
||||
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
|
||||
@@ -40452,6 +40456,7 @@ package android.service.notification {
|
||||
method public final void snoozeNotification(java.lang.String, java.lang.String);
|
||||
method public final void snoozeNotification(java.lang.String, long);
|
||||
method public void unregisterAsSystemService() throws android.os.RemoteException;
|
||||
method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
|
||||
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
|
||||
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
|
||||
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
|
||||
@@ -40460,6 +40465,9 @@ package android.service.notification {
|
||||
field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
|
||||
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
|
||||
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
|
||||
field public static final int REASON_APP_CANCEL = 8; // 0x8
|
||||
field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
|
||||
field public static final int REASON_CANCEL = 2; // 0x2
|
||||
|
||||
@@ -37491,12 +37491,16 @@ package android.service.notification {
|
||||
method public final int getCurrentInterruptionFilter();
|
||||
method public final int getCurrentListenerHints();
|
||||
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
|
||||
method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
|
||||
method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
|
||||
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
|
||||
method public android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onInterruptionFilterChanged(int);
|
||||
method public void onListenerConnected();
|
||||
method public void onListenerDisconnected();
|
||||
method public void onListenerHintsChanged(int);
|
||||
method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
|
||||
method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
|
||||
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
|
||||
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
|
||||
@@ -37510,6 +37514,7 @@ package android.service.notification {
|
||||
method public final void setNotificationsShown(java.lang.String[]);
|
||||
method public final void snoozeNotification(java.lang.String, java.lang.String);
|
||||
method public final void snoozeNotification(java.lang.String, long);
|
||||
method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
|
||||
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
|
||||
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
|
||||
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
|
||||
@@ -37518,6 +37523,9 @@ package android.service.notification {
|
||||
field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
|
||||
field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
|
||||
field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3
|
||||
field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2
|
||||
field public static final int REASON_APP_CANCEL = 8; // 0x8
|
||||
field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
|
||||
field public static final int REASON_CANCEL = 2; // 0x2
|
||||
|
||||
@@ -101,6 +101,10 @@ interface INotificationManager
|
||||
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
|
||||
void setInterruptionFilter(String pkg, int interruptionFilter);
|
||||
|
||||
void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in NotificationChannel channel);
|
||||
ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg);
|
||||
ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg);
|
||||
|
||||
void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
|
||||
void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
|
||||
void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.service.notification;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.service.notification.IStatusBarNotificationHolder;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.service.notification.NotificationRankingUpdate;
|
||||
@@ -33,6 +35,10 @@ oneway interface INotificationListener
|
||||
void onListenerHintsChanged(int hints);
|
||||
void onInterruptionFilterChanged(int interruptionFilter);
|
||||
|
||||
// companion device managers only
|
||||
void onNotificationChannelModification(String pkgName, in NotificationChannel channel, int modificationType);
|
||||
void onNotificationChannelGroupModification(String pkgName, in NotificationChannelGroup group, int modificationType);
|
||||
|
||||
// rankers only
|
||||
void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder);
|
||||
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
|
||||
|
||||
@@ -138,7 +138,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
|
||||
@Override
|
||||
public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package android.service.notification;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
@@ -51,6 +56,8 @@ import android.widget.RemoteViews;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -59,7 +66,7 @@ import java.util.List;
|
||||
* A service that receives calls from the system when new notifications are
|
||||
* posted or removed, or their ranking changed.
|
||||
* <p>To extend this class, you must declare the service in your manifest file with
|
||||
* the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
|
||||
* the {@link Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
|
||||
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
|
||||
* <pre>
|
||||
* <service android:name=".NotificationListener"
|
||||
@@ -215,6 +222,37 @@ public abstract class NotificationListenerService extends Service {
|
||||
@SystemApi
|
||||
public static final int TRIM_LIGHT = 1;
|
||||
|
||||
|
||||
/** @hide */
|
||||
@IntDef({NOTIFICATION_CHANNEL_OR_GROUP_ADDED, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
|
||||
NOTIFICATION_CHANNEL_OR_GROUP_DELETED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ChannelOrGroupModificationTypes {}
|
||||
|
||||
/**
|
||||
* Channel or group modification reason provided to
|
||||
* {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
|
||||
* {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
|
||||
* provided object was created.
|
||||
*/
|
||||
public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1;
|
||||
|
||||
/**
|
||||
* Channel or group modification reason provided to
|
||||
* {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
|
||||
* {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
|
||||
* provided object was updated.
|
||||
*/
|
||||
public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2;
|
||||
|
||||
/**
|
||||
* Channel or group modification reason provided to
|
||||
* {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
|
||||
* {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
|
||||
* provided object was deleted.
|
||||
*/
|
||||
public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private Handler mHandler;
|
||||
@@ -387,6 +425,40 @@ public abstract class NotificationListenerService extends Service {
|
||||
// optional
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to learn about notification channel modifications.
|
||||
*
|
||||
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
|
||||
* device} in order to receive this callback.
|
||||
*
|
||||
* @param pkg The package the channel belongs to.
|
||||
* @param channel The channel that has changed.
|
||||
* @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
|
||||
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
|
||||
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
|
||||
*/
|
||||
public void onNotificationChannelModified(String pkg, NotificationChannel channel,
|
||||
@ChannelOrGroupModificationTypes int modificationType) {
|
||||
// optional
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to learn about notification channel group modifications.
|
||||
*
|
||||
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
|
||||
* device} in order to receive this callback.
|
||||
*
|
||||
* @param pkg The package the group belongs to.
|
||||
* @param group The group that has changed.
|
||||
* @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
|
||||
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
|
||||
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
|
||||
*/
|
||||
public void onNotificationChannelGroupModified(String pkg, NotificationChannelGroup group,
|
||||
@ChannelOrGroupModificationTypes int modificationType) {
|
||||
// optional
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this method to be notified when the
|
||||
* {@link #getCurrentInterruptionFilter() interruption filter} changed.
|
||||
@@ -587,6 +659,69 @@ public abstract class NotificationListenerService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates a notification channel for a given package. This should only be used to reflect
|
||||
* changes a user has made to the channel via the listener's user interface.
|
||||
*
|
||||
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
|
||||
* device} in order to use this method.
|
||||
*
|
||||
* @param pkg The package the channel belongs to.
|
||||
* @param channel the channel to update.
|
||||
*/
|
||||
public final void updateNotificationChannel(@NonNull String pkg,
|
||||
@NonNull NotificationChannel channel) {
|
||||
if (!isBound()) return;
|
||||
try {
|
||||
getNotificationInterface().updateNotificationChannelFromPrivilegedListener(
|
||||
mWrapper, pkg, channel);
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Unable to contact notification manager", e);
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all notification channels belonging to the given package.
|
||||
*
|
||||
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
|
||||
* device} in order to use this method.
|
||||
*
|
||||
* @param pkg The package to retrieve channels for.
|
||||
*/
|
||||
public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg) {
|
||||
if (!isBound()) return null;
|
||||
try {
|
||||
|
||||
return getNotificationInterface().getNotificationChannelsFromPrivilegedListener(
|
||||
mWrapper, pkg).getList();
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Unable to contact notification manager", e);
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all notification channel groups belonging to the given package.
|
||||
*
|
||||
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
|
||||
* device} in order to use this method.
|
||||
*
|
||||
* @param pkg The package to retrieve channel groups for.
|
||||
*/
|
||||
public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg) {
|
||||
if (!isBound()) return null;
|
||||
try {
|
||||
|
||||
return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener(
|
||||
mWrapper, pkg).getList();
|
||||
} catch (RemoteException e) {
|
||||
Log.v(TAG, "Unable to contact notification manager", e);
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the notification trim that will be received via {@link #onNotificationPosted}.
|
||||
*
|
||||
@@ -1116,6 +1251,28 @@ public abstract class NotificationListenerService extends Service {
|
||||
// no-op in the listener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationChannelModification(String pkgName, NotificationChannel channel,
|
||||
@ChannelOrGroupModificationTypes int modificationType) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = pkgName;
|
||||
args.arg2 = channel;
|
||||
args.arg3 = modificationType;
|
||||
mHandler.obtainMessage(
|
||||
MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationChannelGroupModification(String pkgName,
|
||||
NotificationChannelGroup group,
|
||||
@ChannelOrGroupModificationTypes int modificationType) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = pkgName;
|
||||
args.arg2 = group;
|
||||
args.arg3 = modificationType;
|
||||
mHandler.obtainMessage(
|
||||
MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1632,6 +1789,8 @@ public abstract class NotificationListenerService extends Service {
|
||||
public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
|
||||
public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
|
||||
public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
|
||||
public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
|
||||
public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
|
||||
|
||||
public MyHandler(Looper looper) {
|
||||
super(looper, null, false);
|
||||
@@ -1678,6 +1837,22 @@ public abstract class NotificationListenerService extends Service {
|
||||
final int interruptionFilter = msg.arg1;
|
||||
onInterruptionFilterChanged(interruptionFilter);
|
||||
} break;
|
||||
|
||||
case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
String pkgName = (String) args.arg1;
|
||||
NotificationChannel channel = (NotificationChannel) args.arg2;
|
||||
int modificationType = (int) args.arg3;
|
||||
onNotificationChannelModified(pkgName, channel, modificationType);
|
||||
} break;
|
||||
|
||||
case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
String pkgName = (String) args.arg1;
|
||||
NotificationChannelGroup group = (NotificationChannelGroup) args.arg2;
|
||||
int modificationType = (int) args.arg3;
|
||||
onNotificationChannelGroupModified(pkgName, group, modificationType);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,13 @@ abstract public class ManagedServices {
|
||||
|
||||
abstract protected void onServiceAdded(ManagedServiceInfo info);
|
||||
|
||||
protected List<ManagedServiceInfo> getServices() {
|
||||
synchronized (mMutex) {
|
||||
List<ManagedServiceInfo> services = new ArrayList<>(mServices);
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
|
||||
|
||||
private ManagedServiceInfo newServiceInfo(IInterface service,
|
||||
|
||||
@@ -19,6 +19,12 @@ package com.android.server.notification;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
|
||||
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
|
||||
import static android.service.notification.NotificationListenerService
|
||||
.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
|
||||
import static android.service.notification.NotificationListenerService
|
||||
.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
|
||||
import static android.service.notification.NotificationListenerService
|
||||
.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
|
||||
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
|
||||
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
|
||||
import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
|
||||
@@ -70,6 +76,7 @@ import android.app.PendingIntent;
|
||||
import android.app.StatusBarManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.companion.ICompanionDeviceManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
@@ -100,6 +107,7 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
@@ -144,6 +152,7 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -260,6 +269,7 @@ public class NotificationManagerService extends SystemService {
|
||||
Vibrator mVibrator;
|
||||
private WindowManagerInternal mWindowManagerInternal;
|
||||
private AlarmManager mAlarmManager;
|
||||
private ICompanionDeviceManager mCompanionManager;
|
||||
|
||||
final IBinder mForegroundToken = new Binder();
|
||||
private Handler mHandler;
|
||||
@@ -1004,7 +1014,8 @@ public class NotificationManagerService extends SystemService {
|
||||
// TODO: Tests should call onStart instead once the methods above are removed.
|
||||
@VisibleForTesting
|
||||
void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
|
||||
LightsManager lightsManager, NotificationListeners notificationListeners) {
|
||||
LightsManager lightsManager, NotificationListeners notificationListeners,
|
||||
ICompanionDeviceManager companionManager) {
|
||||
Resources resources = getContext().getResources();
|
||||
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
|
||||
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
|
||||
@@ -1017,6 +1028,7 @@ public class NotificationManagerService extends SystemService {
|
||||
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
|
||||
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
|
||||
mCompanionManager = companionManager;
|
||||
|
||||
mHandler = new WorkerHandler(looper);
|
||||
mRankingThread.start();
|
||||
@@ -1192,7 +1204,8 @@ public class NotificationManagerService extends SystemService {
|
||||
@Override
|
||||
public void onStart() {
|
||||
init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
|
||||
getLocalService(LightsManager.class), new NotificationListeners());
|
||||
getLocalService(LightsManager.class), new NotificationListeners(),
|
||||
null);
|
||||
publishBinderService(Context.NOTIFICATION_SERVICE, mService);
|
||||
publishLocalService(NotificationManagerInternal.class, mInternalService);
|
||||
}
|
||||
@@ -1294,7 +1307,8 @@ public class NotificationManagerService extends SystemService {
|
||||
sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
|
||||
}
|
||||
|
||||
private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel) {
|
||||
private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
|
||||
boolean fromListener) {
|
||||
if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
|
||||
// cancel
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
|
||||
@@ -1303,6 +1317,14 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
mRankingHelper.updateNotificationChannel(pkg, uid, channel);
|
||||
|
||||
final NotificationChannel modifiedChannel =
|
||||
mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
|
||||
|
||||
if (!fromListener) {
|
||||
mListeners.notifyNotificationChannelChanged(
|
||||
pkg, modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
|
||||
}
|
||||
|
||||
synchronized (mNotificationLock) {
|
||||
final int N = mNotificationList.size();
|
||||
for (int i = N - 1; i >= 0; --i) {
|
||||
@@ -1311,8 +1333,7 @@ public class NotificationManagerService extends SystemService {
|
||||
&& r.sbn.getUid() == uid
|
||||
&& channel.getId() != null
|
||||
&& channel.getId().equals(r.getChannel().getId())) {
|
||||
r.updateNotificationChannel(mRankingHelper.getNotificationChannel(
|
||||
pkg, uid, channel.getId(), false));
|
||||
r.updateNotificationChannel(modifiedChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1626,6 +1647,8 @@ public class NotificationManagerService extends SystemService {
|
||||
Preconditions.checkNotNull(group, "group in list is null");
|
||||
mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
|
||||
true /* fromTargetApp */);
|
||||
mListeners.notifyNotificationChannelGroupChanged(pkg, group,
|
||||
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
|
||||
}
|
||||
savePolicyFile();
|
||||
}
|
||||
@@ -1639,6 +1662,9 @@ public class NotificationManagerService extends SystemService {
|
||||
Preconditions.checkNotNull(channel, "channel in list is null");
|
||||
mRankingHelper.createNotificationChannel(pkg, uid, channel,
|
||||
true /* fromTargetApp */);
|
||||
mListeners.notifyNotificationChannelChanged(pkg,
|
||||
mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
|
||||
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
|
||||
}
|
||||
savePolicyFile();
|
||||
}
|
||||
@@ -1674,12 +1700,16 @@ public class NotificationManagerService extends SystemService {
|
||||
@Override
|
||||
public void deleteNotificationChannel(String pkg, String channelId) {
|
||||
checkCallerIsSystemOrSameApp(pkg);
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
|
||||
throw new IllegalArgumentException("Cannot delete default channel");
|
||||
}
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
|
||||
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
|
||||
mRankingHelper.deleteNotificationChannel(pkg, Binder.getCallingUid(), channelId);
|
||||
UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
|
||||
mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
|
||||
mListeners.notifyNotificationChannelChanged(pkg,
|
||||
mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
|
||||
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
|
||||
savePolicyFile();
|
||||
}
|
||||
|
||||
@@ -1692,16 +1722,28 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteNotificationChannelGroup(String pkg, String channelGroupId) {
|
||||
public void deleteNotificationChannelGroup(String pkg, String groupId) {
|
||||
checkCallerIsSystemOrSameApp(pkg);
|
||||
|
||||
List<String> deletedChannelIds = mRankingHelper.deleteNotificationChannelGroup(
|
||||
pkg, Binder.getCallingUid(), channelGroupId);
|
||||
for (int i = 0; i < deletedChannelIds.size(); i++) {
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannelIds.get(i), 0, 0, true,
|
||||
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
NotificationChannelGroup groupToDelete =
|
||||
mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
|
||||
if (groupToDelete != null) {
|
||||
List<NotificationChannel> deletedChannels =
|
||||
mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
|
||||
for (int i = 0; i < deletedChannels.size(); i++) {
|
||||
final NotificationChannel deletedChannel = deletedChannels.get(i);
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
|
||||
true,
|
||||
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
|
||||
null);
|
||||
mListeners.notifyNotificationChannelChanged(pkg, deletedChannel,
|
||||
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
|
||||
}
|
||||
mListeners.notifyNotificationChannelGroupChanged(
|
||||
pkg, groupToDelete, NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
|
||||
savePolicyFile();
|
||||
}
|
||||
savePolicyFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1709,7 +1751,7 @@ public class NotificationManagerService extends SystemService {
|
||||
NotificationChannel channel) {
|
||||
enforceSystemOrSystemUI("Caller not system or systemui");
|
||||
Preconditions.checkNotNull(channel);
|
||||
updateNotificationChannelInt(pkg, uid, channel);
|
||||
updateNotificationChannelInt(pkg, uid, channel, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2646,6 +2688,41 @@ public class NotificationManagerService extends SystemService {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
|
||||
String pkg, NotificationChannel channel) throws RemoteException {
|
||||
Preconditions.checkNotNull(channel);
|
||||
|
||||
ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
|
||||
checkHasCompanionDevice(info);
|
||||
|
||||
int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
|
||||
updateNotificationChannelInt(pkg, uid, channel, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
|
||||
INotificationListener token, String pkg) throws RemoteException {
|
||||
ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
|
||||
checkHasCompanionDevice(info);
|
||||
|
||||
int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
|
||||
return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice<NotificationChannelGroup>
|
||||
getNotificationChannelGroupsFromPrivilegedListener(
|
||||
INotificationListener token, String pkg) throws RemoteException {
|
||||
ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
|
||||
checkHasCompanionDevice(info);
|
||||
|
||||
List<NotificationChannelGroup> groups = new ArrayList<>();
|
||||
int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
|
||||
groups.addAll(mRankingHelper.getNotificationChannelGroups(pkg, uid));
|
||||
return new ParceledListSlice<>(groups);
|
||||
}
|
||||
};
|
||||
|
||||
private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
|
||||
@@ -4437,11 +4514,11 @@ public class NotificationManagerService extends SystemService {
|
||||
return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
|
||||
}
|
||||
|
||||
private boolean isCallerSystem() {
|
||||
protected boolean isCallerSystem() {
|
||||
return isUidSystem(Binder.getCallingUid());
|
||||
}
|
||||
|
||||
protected void checkCallerIsSystem() {
|
||||
private void checkCallerIsSystem() {
|
||||
if (isCallerSystem()) {
|
||||
return;
|
||||
}
|
||||
@@ -4570,6 +4647,17 @@ public class NotificationManagerService extends SystemService {
|
||||
channels, overridePeople, snoozeCriteria, showBadge);
|
||||
}
|
||||
|
||||
private void checkHasCompanionDevice(ManagedServiceInfo info) throws RemoteException {
|
||||
if (mCompanionManager == null) {
|
||||
mCompanionManager = ICompanionDeviceManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
|
||||
}
|
||||
if (ArrayUtils.isEmpty(mCompanionManager.getAssociations(
|
||||
info.component.getPackageName(), info.userid))) {
|
||||
throw new SecurityException("Disallowed call from " + info.component);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
|
||||
if (!listener.enabledAndUserMatches(sbn.getUserId())) {
|
||||
return false;
|
||||
@@ -4656,7 +4744,6 @@ public class NotificationManagerService extends SystemService {
|
||||
final StatusBarNotification sbn = r.sbn;
|
||||
TrimCache trimCache = new TrimCache(sbn);
|
||||
|
||||
// mServices is the list inside ManagedServices of all the assistants,
|
||||
// There should be only one, but it's a list, so while we enforce
|
||||
// singularity elsewhere, we keep it general here, to avoid surprises.
|
||||
for (final ManagedServiceInfo info : NotificationAssistants.this.mServices) {
|
||||
@@ -4895,6 +4982,58 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyNotificationChannelChanged(final String pkg,
|
||||
final NotificationChannel channel, final int modificationType) {
|
||||
if (channel == null) {
|
||||
return;
|
||||
}
|
||||
for (final ManagedServiceInfo serviceInfo : getServices()) {
|
||||
if (!serviceInfo.isEnabledForCurrentProfiles()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
checkHasCompanionDevice(serviceInfo);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyNotificationChannelChanged(serviceInfo, pkg, channel,
|
||||
modificationType);
|
||||
}
|
||||
});
|
||||
} catch (SecurityException se) {
|
||||
// Not a privileged listener; do not notify
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Cannot reach companion device service", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyNotificationChannelGroupChanged(final String pkg,
|
||||
final NotificationChannelGroup group, final int modificationType) {
|
||||
if (group == null) {
|
||||
return;
|
||||
}
|
||||
for (final ManagedServiceInfo serviceInfo : getServices()) {
|
||||
if (!serviceInfo.isEnabledForCurrentProfiles()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
checkHasCompanionDevice(serviceInfo);
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyNotificationChannelGroupChanged(serviceInfo, pkg, group,
|
||||
modificationType);
|
||||
}
|
||||
});
|
||||
} catch (SecurityException se) {
|
||||
// Not a privileged listener; do not notify
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Cannot reach companion device service", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyPosted(final ManagedServiceInfo info,
|
||||
final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
|
||||
final INotificationListener listener = (INotificationListener) info.service;
|
||||
@@ -4949,6 +5088,28 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
void notifyNotificationChannelChanged(ManagedServiceInfo info,
|
||||
final String pkg, final NotificationChannel channel,
|
||||
final int modificationType) {
|
||||
final INotificationListener listener = (INotificationListener) info.service;
|
||||
try {
|
||||
listener.onNotificationChannelModification(pkg, channel, modificationType);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
|
||||
final String pkg, final NotificationChannelGroup group,
|
||||
final int modificationType) {
|
||||
final INotificationListener listener = (INotificationListener) info.service;
|
||||
try {
|
||||
listener.onNotificationChannelGroupModification(pkg, group, modificationType);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isListenerPackage(String packageName) {
|
||||
if (packageName == null) {
|
||||
return false;
|
||||
|
||||
@@ -715,12 +715,12 @@ public class RankingHelper implements RankingConfig {
|
||||
return new ParceledListSlice<>(new ArrayList<>(groups.values()));
|
||||
}
|
||||
|
||||
public List<String> deleteNotificationChannelGroup(String pkg, int uid,
|
||||
public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
|
||||
String groupId) {
|
||||
List<String> deletedChannelIds = new ArrayList<>();
|
||||
List<NotificationChannel> deletedChannels = new ArrayList<>();
|
||||
Record r = getRecord(pkg, uid);
|
||||
if (r == null || TextUtils.isEmpty(groupId)) {
|
||||
return deletedChannelIds;
|
||||
return deletedChannels;
|
||||
}
|
||||
|
||||
r.groups.remove(groupId);
|
||||
@@ -730,11 +730,11 @@ public class RankingHelper implements RankingConfig {
|
||||
final NotificationChannel nc = r.channels.valueAt(i);
|
||||
if (groupId.equals(nc.getGroup())) {
|
||||
nc.setDeleted(true);
|
||||
deletedChannelIds.add(nc.getId());
|
||||
deletedChannels.add(nc);
|
||||
}
|
||||
}
|
||||
updateConfig();
|
||||
return deletedChannelIds;
|
||||
return deletedChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.notification;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.Assert.fail;
|
||||
@@ -26,6 +28,8 @@ import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -33,25 +37,29 @@ import static org.mockito.Mockito.when;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.companion.ICompanionDeviceManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.Color;
|
||||
import android.os.Binder;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.annotation.UiThreadTest;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -74,17 +82,24 @@ public class NotificationManagerServiceTest {
|
||||
private final RankingHelper mRankingHelper = mock(RankingHelper.class);
|
||||
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
|
||||
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
private NotificationManagerService.NotificationListeners mNotificationListeners =
|
||||
mock(NotificationManagerService.NotificationListeners.class);
|
||||
private ManagedServices.ManagedServiceInfo mListener =
|
||||
mNotificationListeners.new ManagedServiceInfo(
|
||||
null, new ComponentName(PKG, "test_class"), uid, true, null, 0);
|
||||
private ICompanionDeviceManager mCompanionMgr = mock(ICompanionDeviceManager.class);
|
||||
|
||||
// Use a Testable subclass so we can simulate calls from the system without failing.
|
||||
private static class TestableNotificationManagerService extends NotificationManagerService {
|
||||
public TestableNotificationManagerService(Context context) { super(context); }
|
||||
|
||||
@Override
|
||||
protected void checkCallerIsSystem() {}
|
||||
protected boolean isCallerSystem() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void setUp() throws Exception {
|
||||
mNotificationManagerService = new TestableNotificationManagerService(mContext);
|
||||
@@ -92,7 +107,7 @@ public class NotificationManagerServiceTest {
|
||||
// MockPackageManager - default returns ApplicationInfo with matching calling UID
|
||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
applicationInfo.uid = uid;
|
||||
when(mPackageManager.getApplicationInfo(any(), anyInt(), anyInt()))
|
||||
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(applicationInfo);
|
||||
when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
|
||||
.thenReturn(applicationInfo);
|
||||
@@ -100,15 +115,10 @@ public class NotificationManagerServiceTest {
|
||||
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
|
||||
// Use this testable looper.
|
||||
mTestableLooper = new TestableLooper(false);
|
||||
// Mock NotificationListeners to bypass security checks.
|
||||
final NotificationManagerService.NotificationListeners mockNotificationListeners =
|
||||
mock(NotificationManagerService.NotificationListeners.class);
|
||||
when(mockNotificationListeners.checkServiceTokenLocked(any())).thenReturn(
|
||||
mockNotificationListeners.new ManagedServiceInfo(null,
|
||||
new ComponentName(PKG, "test_class"), uid, true, null, 0));
|
||||
|
||||
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
|
||||
mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
|
||||
mPackageManagerClient, mockLightsManager, mockNotificationListeners);
|
||||
mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr);
|
||||
|
||||
// Tests call directly into the Binder.
|
||||
mBinderService = mNotificationManagerService.getBinderService();
|
||||
@@ -419,4 +429,206 @@ public class NotificationManagerServiceTest {
|
||||
verify(mRankingHelper, times(1)).getNotificationChannel(
|
||||
anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCreateChannelNotifyListener() throws Exception {
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
|
||||
eq(mTestNotificationChannel.getId()), anyBoolean()))
|
||||
.thenReturn(mTestNotificationChannel);
|
||||
NotificationChannel channel2 = new NotificationChannel("a", "b", IMPORTANCE_LOW);
|
||||
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
|
||||
eq(channel2.getId()), anyBoolean()))
|
||||
.thenReturn(channel2);
|
||||
|
||||
reset(mNotificationListeners);
|
||||
mBinderService.createNotificationChannels(PKG,
|
||||
new ParceledListSlice(Arrays.asList(mTestNotificationChannel, channel2)));
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(mTestNotificationChannel),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(channel2),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCreateChannelGroupNotifyListener() throws Exception {
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
|
||||
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
|
||||
|
||||
reset(mNotificationListeners);
|
||||
mBinderService.createNotificationChannelGroups(PKG,
|
||||
new ParceledListSlice(Arrays.asList(group1, group2)));
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
|
||||
eq(group1),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
|
||||
eq(group2),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testUpdateChannelNotifyListener() throws Exception {
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
mTestNotificationChannel.setLightColor(Color.CYAN);
|
||||
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
|
||||
eq(mTestNotificationChannel.getId()), anyBoolean()))
|
||||
.thenReturn(mTestNotificationChannel);
|
||||
|
||||
reset(mNotificationListeners);
|
||||
mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(mTestNotificationChannel),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testDeleteChannelNotifyListener() throws Exception {
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
|
||||
eq(mTestNotificationChannel.getId()), anyBoolean()))
|
||||
.thenReturn(mTestNotificationChannel);
|
||||
reset(mNotificationListeners);
|
||||
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(mTestNotificationChannel),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testDeleteChannelGroupNotifyListener() throws Exception {
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
|
||||
.thenReturn(ncg);
|
||||
reset(mNotificationListeners);
|
||||
mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
|
||||
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
|
||||
eq(ncg),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
mBinderService.updateNotificationChannelFromPrivilegedListener(
|
||||
null, PKG, mTestNotificationChannel);
|
||||
|
||||
verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any());
|
||||
|
||||
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(mTestNotificationChannel),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
try {
|
||||
mBinderService.updateNotificationChannelFromPrivilegedListener(
|
||||
null, PKG, mTestNotificationChannel);
|
||||
fail("listeners that don't have a companion device shouldn't be able to call this");
|
||||
} catch (SecurityException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
|
||||
|
||||
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
|
||||
eq(mTestNotificationChannel),
|
||||
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG);
|
||||
|
||||
verify(mRankingHelper, times(1)).getNotificationChannels(
|
||||
anyString(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
try {
|
||||
mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG);
|
||||
fail("listeners that don't have a companion device shouldn't be able to call this");
|
||||
} catch (SecurityException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
verify(mRankingHelper, never()).getNotificationChannels(
|
||||
anyString(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
associations.add("a");
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG);
|
||||
|
||||
verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
|
||||
mNotificationManagerService.setRankingHelper(mRankingHelper);
|
||||
List<String> associations = new ArrayList<>();
|
||||
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
|
||||
|
||||
try {
|
||||
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG);
|
||||
fail("listeners that don't have a companion device shouldn't be able to call this");
|
||||
} catch (SecurityException e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user