Merge "Add logging for zen mode events." into udc-dev am: 34bc8be6c5

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22605299

Change-Id: I89230c191827cf6a4bc21855bba6069333b3b1d7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Yuri Lin
2023-05-16 20:43:03 +00:00
committed by Automerger Merge Worker
13 changed files with 2251 additions and 526 deletions

View File

@@ -205,12 +205,24 @@ public class ZenModeDiff {
private final ArrayMap<String, RuleDiff> mAutomaticRulesDiff = new ArrayMap<>();
private RuleDiff mManualRuleDiff;
// Helpers for string generation
private static final String ALLOW_CALLS_FROM_FIELD = "allowCallsFrom";
private static final String ALLOW_MESSAGES_FROM_FIELD = "allowMessagesFrom";
private static final String ALLOW_CONVERSATIONS_FROM_FIELD = "allowConversationsFrom";
// Field name constants
public static final String FIELD_USER = "user";
public static final String FIELD_ALLOW_ALARMS = "allowAlarms";
public static final String FIELD_ALLOW_MEDIA = "allowMedia";
public static final String FIELD_ALLOW_SYSTEM = "allowSystem";
public static final String FIELD_ALLOW_CALLS = "allowCalls";
public static final String FIELD_ALLOW_REMINDERS = "allowReminders";
public static final String FIELD_ALLOW_EVENTS = "allowEvents";
public static final String FIELD_ALLOW_REPEAT_CALLERS = "allowRepeatCallers";
public static final String FIELD_ALLOW_MESSAGES = "allowMessages";
public static final String FIELD_ALLOW_CONVERSATIONS = "allowConversations";
public static final String FIELD_ALLOW_CALLS_FROM = "allowCallsFrom";
public static final String FIELD_ALLOW_MESSAGES_FROM = "allowMessagesFrom";
public static final String FIELD_ALLOW_CONVERSATIONS_FROM = "allowConversationsFrom";
public static final String FIELD_SUPPRESSED_VISUAL_EFFECTS = "suppressedVisualEffects";
public static final String FIELD_ARE_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
private static final Set<String> PEOPLE_TYPE_FIELDS =
Set.of(ALLOW_CALLS_FROM_FIELD, ALLOW_MESSAGES_FROM_FIELD);
Set.of(FIELD_ALLOW_CALLS_FROM, FIELD_ALLOW_MESSAGES_FROM);
/**
* Create a diff that contains diffs between the "from" and "to" ZenModeConfigs.
@@ -232,57 +244,57 @@ public class ZenModeDiff {
// Now we compare all the fields, knowing there's a diff and that neither is null
if (from.user != to.user) {
addField("user", new FieldDiff<>(from.user, to.user));
addField(FIELD_USER, new FieldDiff<>(from.user, to.user));
}
if (from.allowAlarms != to.allowAlarms) {
addField("allowAlarms", new FieldDiff<>(from.allowAlarms, to.allowAlarms));
addField(FIELD_ALLOW_ALARMS, new FieldDiff<>(from.allowAlarms, to.allowAlarms));
}
if (from.allowMedia != to.allowMedia) {
addField("allowMedia", new FieldDiff<>(from.allowMedia, to.allowMedia));
addField(FIELD_ALLOW_MEDIA, new FieldDiff<>(from.allowMedia, to.allowMedia));
}
if (from.allowSystem != to.allowSystem) {
addField("allowSystem", new FieldDiff<>(from.allowSystem, to.allowSystem));
addField(FIELD_ALLOW_SYSTEM, new FieldDiff<>(from.allowSystem, to.allowSystem));
}
if (from.allowCalls != to.allowCalls) {
addField("allowCalls", new FieldDiff<>(from.allowCalls, to.allowCalls));
addField(FIELD_ALLOW_CALLS, new FieldDiff<>(from.allowCalls, to.allowCalls));
}
if (from.allowReminders != to.allowReminders) {
addField("allowReminders",
addField(FIELD_ALLOW_REMINDERS,
new FieldDiff<>(from.allowReminders, to.allowReminders));
}
if (from.allowEvents != to.allowEvents) {
addField("allowEvents", new FieldDiff<>(from.allowEvents, to.allowEvents));
addField(FIELD_ALLOW_EVENTS, new FieldDiff<>(from.allowEvents, to.allowEvents));
}
if (from.allowRepeatCallers != to.allowRepeatCallers) {
addField("allowRepeatCallers",
addField(FIELD_ALLOW_REPEAT_CALLERS,
new FieldDiff<>(from.allowRepeatCallers, to.allowRepeatCallers));
}
if (from.allowMessages != to.allowMessages) {
addField("allowMessages",
addField(FIELD_ALLOW_MESSAGES,
new FieldDiff<>(from.allowMessages, to.allowMessages));
}
if (from.allowConversations != to.allowConversations) {
addField("allowConversations",
addField(FIELD_ALLOW_CONVERSATIONS,
new FieldDiff<>(from.allowConversations, to.allowConversations));
}
if (from.allowCallsFrom != to.allowCallsFrom) {
addField("allowCallsFrom",
addField(FIELD_ALLOW_CALLS_FROM,
new FieldDiff<>(from.allowCallsFrom, to.allowCallsFrom));
}
if (from.allowMessagesFrom != to.allowMessagesFrom) {
addField("allowMessagesFrom",
addField(FIELD_ALLOW_MESSAGES_FROM,
new FieldDiff<>(from.allowMessagesFrom, to.allowMessagesFrom));
}
if (from.allowConversationsFrom != to.allowConversationsFrom) {
addField("allowConversationsFrom",
addField(FIELD_ALLOW_CONVERSATIONS_FROM,
new FieldDiff<>(from.allowConversationsFrom, to.allowConversationsFrom));
}
if (from.suppressedVisualEffects != to.suppressedVisualEffects) {
addField("suppressedVisualEffects",
addField(FIELD_SUPPRESSED_VISUAL_EFFECTS,
new FieldDiff<>(from.suppressedVisualEffects, to.suppressedVisualEffects));
}
if (from.areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
addField("areChannelsBypassingDnd",
addField(FIELD_ARE_CHANNELS_BYPASSING_DND,
new FieldDiff<>(from.areChannelsBypassingDnd, to.areChannelsBypassingDnd));
}
@@ -366,7 +378,7 @@ public class ZenModeDiff {
sb.append(ZenModeConfig.sourceToString((int) diff.from()));
sb.append("->");
sb.append(ZenModeConfig.sourceToString((int) diff.to()));
} else if (key.equals(ALLOW_CONVERSATIONS_FROM_FIELD)) {
} else if (key.equals(FIELD_ALLOW_CONVERSATIONS_FROM)) {
sb.append(key);
sb.append(":");
sb.append(ZenPolicy.conversationTypeToString((int) diff.from()));
@@ -428,6 +440,24 @@ public class ZenModeDiff {
* Diff class representing a change between two ZenRules.
*/
public static class RuleDiff extends BaseDiff {
public static final String FIELD_ENABLED = "enabled";
public static final String FIELD_SNOOZING = "snoozing";
public static final String FIELD_NAME = "name";
public static final String FIELD_ZEN_MODE = "zenMode";
public static final String FIELD_CONDITION_ID = "conditionId";
public static final String FIELD_CONDITION = "condition";
public static final String FIELD_COMPONENT = "component";
public static final String FIELD_CONFIGURATION_ACTIVITY = "configurationActivity";
public static final String FIELD_ID = "id";
public static final String FIELD_CREATION_TIME = "creationTime";
public static final String FIELD_ENABLER = "enabler";
public static final String FIELD_ZEN_POLICY = "zenPolicy";
public static final String FIELD_MODIFIED = "modified";
public static final String FIELD_PKG = "pkg";
// Special field to track whether this rule became active or inactive
FieldDiff<Boolean> mActiveDiff;
/**
* Create a RuleDiff representing the difference between two ZenRule objects.
* @param from previous ZenRule
@@ -440,54 +470,64 @@ public class ZenModeDiff {
if (from == null && to == null) {
return;
}
// Even if added or removed, there may be a change in whether or not it was active.
// This only applies to automatic rules.
boolean fromActive = from != null ? from.isAutomaticActive() : false;
boolean toActive = to != null ? to.isAutomaticActive() : false;
if (fromActive != toActive) {
mActiveDiff = new FieldDiff<>(fromActive, toActive);
}
// Return if the diff was added or removed
if (hasExistenceChange()) {
return;
}
if (from.enabled != to.enabled) {
addField("enabled", new FieldDiff<>(from.enabled, to.enabled));
addField(FIELD_ENABLED, new FieldDiff<>(from.enabled, to.enabled));
}
if (from.snoozing != to.snoozing) {
addField("snoozing", new FieldDiff<>(from.snoozing, to.snoozing));
addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
}
if (!Objects.equals(from.name, to.name)) {
addField("name", new FieldDiff<>(from.name, to.name));
addField(FIELD_NAME, new FieldDiff<>(from.name, to.name));
}
if (from.zenMode != to.zenMode) {
addField("zenMode", new FieldDiff<>(from.zenMode, to.zenMode));
addField(FIELD_ZEN_MODE, new FieldDiff<>(from.zenMode, to.zenMode));
}
if (!Objects.equals(from.conditionId, to.conditionId)) {
addField("conditionId", new FieldDiff<>(from.conditionId, to.conditionId));
addField(FIELD_CONDITION_ID, new FieldDiff<>(from.conditionId,
to.conditionId));
}
if (!Objects.equals(from.condition, to.condition)) {
addField("condition", new FieldDiff<>(from.condition, to.condition));
addField(FIELD_CONDITION, new FieldDiff<>(from.condition, to.condition));
}
if (!Objects.equals(from.component, to.component)) {
addField("component", new FieldDiff<>(from.component, to.component));
addField(FIELD_COMPONENT, new FieldDiff<>(from.component, to.component));
}
if (!Objects.equals(from.configurationActivity, to.configurationActivity)) {
addField("configurationActivity", new FieldDiff<>(
addField(FIELD_CONFIGURATION_ACTIVITY, new FieldDiff<>(
from.configurationActivity, to.configurationActivity));
}
if (!Objects.equals(from.id, to.id)) {
addField("id", new FieldDiff<>(from.id, to.id));
addField(FIELD_ID, new FieldDiff<>(from.id, to.id));
}
if (from.creationTime != to.creationTime) {
addField("creationTime",
addField(FIELD_CREATION_TIME,
new FieldDiff<>(from.creationTime, to.creationTime));
}
if (!Objects.equals(from.enabler, to.enabler)) {
addField("enabler", new FieldDiff<>(from.enabler, to.enabler));
addField(FIELD_ENABLER, new FieldDiff<>(from.enabler, to.enabler));
}
if (!Objects.equals(from.zenPolicy, to.zenPolicy)) {
addField("zenPolicy", new FieldDiff<>(from.zenPolicy, to.zenPolicy));
addField(FIELD_ZEN_POLICY, new FieldDiff<>(from.zenPolicy, to.zenPolicy));
}
if (from.modified != to.modified) {
addField("modified", new FieldDiff<>(from.modified, to.modified));
addField(FIELD_MODIFIED, new FieldDiff<>(from.modified, to.modified));
}
if (!Objects.equals(from.pkg, to.pkg)) {
addField("pkg", new FieldDiff<>(from.pkg, to.pkg));
addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg));
}
}
@@ -536,7 +576,35 @@ public class ZenModeDiff {
sb.append(diff);
}
if (becameActive()) {
if (!first) {
sb.append(", ");
}
sb.append("(->active)");
} else if (becameInactive()) {
if (!first) {
sb.append(", ");
}
sb.append("(->inactive)");
}
return sb.append("}").toString();
}
/**
* Returns whether this diff indicates that this (automatic) rule became active.
*/
public boolean becameActive() {
// if the "to" side is true, then it became active
return mActiveDiff != null && mActiveDiff.to();
}
/**
* Returns whether this diff indicates that this (automatic) rule became inactive.
*/
public boolean becameInactive() {
// if the "to" side is false, then it became inactive
return mActiveDiff != null && !mActiveDiff.to();
}
}
}

View File

@@ -360,3 +360,11 @@ message DNDPolicyProto {
optional ConversationType allow_conversations_from = 19;
}
// Enum identifying the type of rule that changed; values set to match ones used in the
// DNDStateChanged proto.
enum RuleType {
RULE_TYPE_UNKNOWN = 0;
RULE_TYPE_MANUAL = 1;
RULE_TYPE_AUTOMATIC = 2;
}

View File

@@ -1652,7 +1652,8 @@ public class NotificationManagerService extends SystemService {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
// update system notification channels
SystemNotificationChannels.createAll(context);
mZenModeHelper.updateDefaultZenRules();
mZenModeHelper.updateDefaultZenRules(Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
mPreferencesHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
}
}
@@ -2280,7 +2281,8 @@ public class NotificationManagerService extends SystemService {
mRankingHandler = rankingHandler;
mConditionProviders = conditionProviders;
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
new SysUiStatsEvent.BuilderFactory());
new SysUiStatsEvent.BuilderFactory(), flagResolver,
new ZenModeEventLogger(mPackageManagerClient));
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
@@ -2862,7 +2864,8 @@ public class NotificationManagerService extends SystemService {
final NotificationChannel preUpdate =
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true,
Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
channel.getImportance() != IMPORTANCE_NONE, true);
@@ -2910,7 +2913,7 @@ public class NotificationManagerService extends SystemService {
final NotificationChannelGroup preUpdate =
mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
mPreferencesHelper.createNotificationChannelGroup(pkg, uid, group,
fromApp);
fromApp, Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
if (!fromApp) {
maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
}
@@ -3875,7 +3878,8 @@ public class NotificationManagerService extends SystemService {
needsPolicyFileChange = mPreferencesHelper.createNotificationChannel(pkg, uid,
channel, true /* fromTargetApp */,
mConditionProviders.isPackageOrComponentAllowed(
pkg, UserHandle.getUserId(uid)));
pkg, UserHandle.getUserId(uid)), Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
if (needsPolicyFileChange) {
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
@@ -4011,6 +4015,7 @@ public class NotificationManagerService extends SystemService {
public void deleteNotificationChannel(String pkg, String channelId) {
checkCallerIsSystemOrSameApp(pkg);
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
final int callingUser = UserHandle.getUserId(callingUid);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
throw new IllegalArgumentException("Cannot delete default channel");
@@ -4020,7 +4025,7 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
callingUser, REASON_CHANNEL_REMOVED, null);
boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
pkg, callingUid, channelId);
pkg, callingUid, channelId, callingUid, isSystemOrSystemUi);
if (previouslyExisted) {
// Remove from both recent notification archive and notification history
mArchive.removeChannelNotifications(pkg, callingUser, channelId);
@@ -4053,6 +4058,7 @@ public class NotificationManagerService extends SystemService {
checkCallerIsSystemOrSameApp(pkg);
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
NotificationChannelGroup groupToDelete =
mPreferencesHelper.getNotificationChannelGroupWithChannels(
pkg, callingUid, groupId, false);
@@ -4066,7 +4072,8 @@ public class NotificationManagerService extends SystemService {
enforceDeletingChannelHasNoUserInitiatedJob(pkg, userId, channelId);
}
List<NotificationChannel> deletedChannels =
mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
mPreferencesHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId,
callingUid, isSystemOrSystemUi);
for (int i = 0; i < deletedChannels.size(); i++) {
final NotificationChannel deletedChannel = deletedChannels.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
@@ -4964,11 +4971,14 @@ public class NotificationManagerService extends SystemService {
@Override
public void requestInterruptionFilterFromListener(INotificationListener token,
int interruptionFilter) throws RemoteException {
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
mZenModeHelper.requestFromListener(info.component, interruptionFilter);
mZenModeHelper.requestFromListener(info.component, interruptionFilter,
callingUid, isSystemOrSystemUi);
updateInterruptionFilterLocked();
}
} finally {
@@ -5008,9 +5018,12 @@ public class NotificationManagerService extends SystemService {
@Override
public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
enforceSystemOrSystemUI("INotificationManager.setZenMode");
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
mZenModeHelper.setManualZenMode(mode, conditionId, null, reason);
mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid,
isSystemOrSystemUi);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5057,7 +5070,8 @@ public class NotificationManagerService extends SystemService {
}
return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
"addAutomaticZenRule");
"addAutomaticZenRule", Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
}
@Override
@@ -5074,7 +5088,8 @@ public class NotificationManagerService extends SystemService {
enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
"updateAutomaticZenRule");
"updateAutomaticZenRule", Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
}
@Override
@@ -5083,7 +5098,8 @@ public class NotificationManagerService extends SystemService {
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule",
Binder.getCallingUid(), isCallerIsSystemOrSystemUi());
}
@Override
@@ -5092,7 +5108,8 @@ public class NotificationManagerService extends SystemService {
enforceSystemOrSystemUI("removeAutomaticZenRules");
return mZenModeHelper.removeAutomaticZenRules(packageName,
packageName + "|removeAutomaticZenRules");
packageName + "|removeAutomaticZenRules", Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
}
@Override
@@ -5110,7 +5127,8 @@ public class NotificationManagerService extends SystemService {
enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
mZenModeHelper.setAutomaticZenRuleState(id, condition);
mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(),
isCallerIsSystemOrSystemUi());
}
@Override
@@ -5118,9 +5136,12 @@ public class NotificationManagerService extends SystemService {
enforcePolicyAccess(pkg, "setInterruptionFilter");
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
final int callingUid = Binder.getCallingUid();
final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter");
mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
callingUid, isSystemOrSystemUi);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5421,6 +5442,7 @@ public class NotificationManagerService extends SystemService {
public void setNotificationPolicy(String pkg, Policy policy) {
enforcePolicyAccess(pkg, "setNotificationPolicy");
int callingUid = Binder.getCallingUid();
boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
final long identity = Binder.clearCallingIdentity();
try {
final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
@@ -5460,7 +5482,7 @@ public class NotificationManagerService extends SystemService {
policy.priorityCallSenders, policy.priorityMessageSenders,
newVisualEffects, policy.priorityConversationSenders);
ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
mZenModeHelper.setNotificationPolicy(policy);
mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
} catch (RemoteException e) {
} finally {
Binder.restoreCallingIdentity(identity);
@@ -6698,7 +6720,8 @@ public class NotificationManagerService extends SystemService {
channel.setUserVisibleTaskShown(true);
}
mPreferencesHelper.updateNotificationChannel(
pkg, notificationUid, channel, false);
pkg, notificationUid, channel, false, callingUid,
isCallerIsSystemOrSystemUi());
r.updateNotificationChannel(channel);
} else if (!channel.isUserVisibleTaskShown() && !TextUtils.isEmpty(channelId)
&& !NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
@@ -6771,7 +6794,8 @@ public class NotificationManagerService extends SystemService {
mHistoryManager.deleteConversations(pkg, uid, shortcuts);
List<String> deletedChannelIds =
mPreferencesHelper.deleteConversations(pkg, uid, shortcuts);
mPreferencesHelper.deleteConversations(pkg, uid, shortcuts,
/* callingUid */ Process.SYSTEM_UID, /* is system */ true);
for (String channelId : deletedChannelIds) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
UserHandle.getUserId(uid), REASON_CHANNEL_REMOVED,
@@ -10000,7 +10024,8 @@ public class NotificationManagerService extends SystemService {
return isUidSystemOrPhone(Binder.getCallingUid());
}
private boolean isCallerIsSystemOrSystemUi() {
@VisibleForTesting
protected boolean isCallerIsSystemOrSystemUi() {
if (isCallerSystemOrPhone()) {
return true;
}

View File

@@ -51,6 +51,7 @@ import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
@@ -217,7 +218,7 @@ public class PreferencesHelper implements RankingConfig {
updateBadgingEnabled();
updateBubblesEnabled();
updateMediaNotificationFilteringEnabled();
syncChannelsBypassingDnd();
syncChannelsBypassingDnd(Process.SYSTEM_UID, true); // init comes from system
}
public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
@@ -834,7 +835,7 @@ public class PreferencesHelper implements RankingConfig {
@Override
public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp) {
boolean fromTargetApp, int callingUid, boolean fromSystemOrSystemUi) {
Objects.requireNonNull(pkg);
Objects.requireNonNull(group);
Objects.requireNonNull(group.getId());
@@ -880,13 +881,14 @@ public class PreferencesHelper implements RankingConfig {
r.groups.put(group.getId(), group);
}
if (needsDndChange) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
}
@Override
public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess) {
boolean fromTargetApp, boolean hasDndAccess, int callingUid,
boolean fromSystemOrSystemUi) {
Objects.requireNonNull(pkg);
Objects.requireNonNull(channel);
Objects.requireNonNull(channel.getId());
@@ -1027,7 +1029,7 @@ public class PreferencesHelper implements RankingConfig {
}
if (needsDndChange) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
return needsPolicyFileChange;
@@ -1056,7 +1058,7 @@ public class PreferencesHelper implements RankingConfig {
@Override
public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
boolean fromUser) {
boolean fromUser, int callingUid, boolean fromSystemOrSystemUi) {
Objects.requireNonNull(updatedChannel);
Objects.requireNonNull(updatedChannel.getId());
boolean changed = false;
@@ -1112,7 +1114,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (needsDndChange) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
if (changed) {
updateConfig();
@@ -1188,7 +1190,8 @@ public class PreferencesHelper implements RankingConfig {
}
@Override
public boolean deleteNotificationChannel(String pkg, int uid, String channelId) {
public boolean deleteNotificationChannel(String pkg, int uid, String channelId,
int callingUid, boolean fromSystemOrSystemUi) {
boolean deletedChannel = false;
boolean channelBypassedDnd = false;
synchronized (mPackagePreferences) {
@@ -1203,7 +1206,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (channelBypassedDnd) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
return deletedChannel;
}
@@ -1394,7 +1397,7 @@ public class PreferencesHelper implements RankingConfig {
}
public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
String groupId) {
String groupId, int callingUid, boolean fromSystemOrSystemUi) {
List<NotificationChannel> deletedChannels = new ArrayList<>();
boolean groupBypassedDnd = false;
synchronized (mPackagePreferences) {
@@ -1420,7 +1423,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (groupBypassedDnd) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
return deletedChannels;
}
@@ -1543,7 +1546,7 @@ public class PreferencesHelper implements RankingConfig {
}
public @NonNull List<String> deleteConversations(String pkg, int uid,
Set<String> conversationIds) {
Set<String> conversationIds, int callingUid, boolean fromSystemOrSystemUi) {
List<String> deletedChannelIds = new ArrayList<>();
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1568,7 +1571,7 @@ public class PreferencesHelper implements RankingConfig {
}
}
if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) {
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
return deletedChannelIds;
}
@@ -1673,18 +1676,18 @@ public class PreferencesHelper implements RankingConfig {
* Syncs {@link #mAreChannelsBypassingDnd} with the current user's notification policy before
* updating
*/
private void syncChannelsBypassingDnd() {
private void syncChannelsBypassingDnd(int callingUid, boolean fromSystemOrSystemUi) {
mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
& NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
updateChannelsBypassingDnd();
updateChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
}
/**
* Updates the user's NotificationPolicy based on whether the current userId
* has channels bypassing DND
*/
private void updateChannelsBypassingDnd() {
private void updateChannelsBypassingDnd(int callingUid, boolean fromSystemOrSystemUi) {
ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
final int currentUserId = getCurrentUser();
@@ -1714,7 +1717,7 @@ public class PreferencesHelper implements RankingConfig {
boolean haveBypassingApps = candidatePkgs.size() > 0;
if (mAreChannelsBypassingDnd != haveBypassingApps) {
mAreChannelsBypassingDnd = haveBypassingApps;
updateZenPolicy(mAreChannelsBypassingDnd);
updateZenPolicy(mAreChannelsBypassingDnd, callingUid, fromSystemOrSystemUi);
}
}
@@ -1739,14 +1742,15 @@ public class PreferencesHelper implements RankingConfig {
return true;
}
public void updateZenPolicy(boolean areChannelsBypassingDnd) {
public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
boolean fromSystemOrSystemUi) {
NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
policy.priorityCategories, policy.priorityCallSenders,
policy.priorityMessageSenders, policy.suppressedVisualEffects,
(areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
: 0),
policy.priorityConversationSenders));
policy.priorityConversationSenders), callingUid, fromSystemOrSystemUi);
}
public boolean areChannelsBypassingDnd() {

View File

@@ -39,19 +39,21 @@ public interface RankingConfig {
Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid);
void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp);
boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi);
ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess);
void updateNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromUser);
boolean fromTargetApp, boolean hasDndAccess, int callingUid,
boolean isSystemOrSystemUi);
void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
boolean fromUser, int callingUid, boolean fromSystemOrSystemUi);
NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted);
NotificationChannel getConversationNotificationChannel(String pkg, int uid, String channelId,
String conversationId, boolean returnParentIfNoConversationChannel,
boolean includeDeleted);
boolean deleteNotificationChannel(String pkg, int uid, String channelId);
boolean deleteNotificationChannel(String pkg, int uid, String channelId,
int callingUid, boolean fromSystemOrSystemUi);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,

View File

@@ -18,6 +18,8 @@ package com.android.server.notification;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
@@ -108,7 +110,9 @@ public class ZenModeConditions implements ConditionProviders.Callback {
@Override
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component);
final int callingUid = Binder.getCallingUid();
mHelper.setConfig(mHelper.getConfig(), component, "zmc.onServiceAdded:" + component,
callingUid, callingUid == Process.SYSTEM_UID);
}
@Override
@@ -116,7 +120,9 @@ public class ZenModeConditions implements ConditionProviders.Callback {
if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
ZenModeConfig config = mHelper.getConfig();
if (config == null) return;
mHelper.setAutomaticZenRuleState(id, condition);
final int callingUid = Binder.getCallingUid();
mHelper.setAutomaticZenRuleState(id, condition, callingUid,
callingUid == Process.SYSTEM_UID);
}
// Only valid for CPS backed rules

View File

@@ -0,0 +1,611 @@
/*
* Copyright (C) 2023 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;
import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
import android.annotation.NonNull;
import android.app.NotificationManager;
import android.content.pm.PackageManager;
import android.os.Process;
import android.service.notification.DNDPolicyProto;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenPolicy;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
import java.io.ByteArrayOutputStream;
import java.util.Objects;
/**
* Class for writing DNDStateChanged atoms to the statsd log.
* Use ZenModeEventLoggerFake for testing.
*/
class ZenModeEventLogger {
private static final String TAG = "ZenModeEventLogger";
// Placeholder int for unknown zen mode, to distinguish from "off".
static final int ZEN_MODE_UNKNOWN = -1;
// Object for tracking config changes and policy changes associated with an overall zen
// mode change.
ZenModeEventLogger.ZenStateChanges mChangeState = new ZenModeEventLogger.ZenStateChanges();
private PackageManager mPm;
ZenModeEventLogger(PackageManager pm) {
mPm = pm;
}
/**
* Enum used to log the type of DND state changed events.
* These use UiEvent IDs for ease of integrating with other UiEvents.
*/
enum ZenStateChangedEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "DND was turned on; may additionally include policy change.")
DND_TURNED_ON(1368),
@UiEvent(doc = "DND was turned off; may additionally include policy change.")
DND_TURNED_OFF(1369),
@UiEvent(doc = "DND policy was changed but the zen mode did not change.")
DND_POLICY_CHANGED(1370),
@UiEvent(doc = "Change in DND automatic rules active, without changing mode or policy.")
DND_ACTIVE_RULES_CHANGED(1371);
private final int mId;
ZenStateChangedEvent(int id) {
mId = id;
}
@Override
public int getId() {
return mId;
}
}
/**
* Potentially log a zen mode change if the provided config and policy changes warrant it.
*
* @param prevInfo ZenModeInfo (zen mode setting, config, policy) prior to this change
* @param newInfo ZenModeInfo after this change takes effect
* @param callingUid the calling UID associated with the change; may be used to attribute the
* change to a particular package or determine if this is a user action
* @param fromSystemOrSystemUi whether the calling UID is either system UID or system UI
*/
public final void maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
boolean fromSystemOrSystemUi) {
mChangeState.init(prevInfo, newInfo, callingUid, fromSystemOrSystemUi);
if (mChangeState.shouldLogChanges()) {
maybeReassignCallingUid();
logChanges();
}
// clear out the state for a fresh start next time
mChangeState = new ZenModeEventLogger.ZenStateChanges();
}
/**
* Reassign callingUid in mChangeState if we have more specific information that warrants it
* (for instance, if the change is automatic and due to an automatic rule change).
*/
private void maybeReassignCallingUid() {
int userId = Process.INVALID_UID;
String packageName = null;
// For a manual rule, we consider reassigning the UID only when the call seems to come from
// the system and there is a non-null enabler in the new config.
// We don't consider the manual rule in the old config because if a manual rule is turning
// off with a call from system, that could easily be a user action to explicitly turn it off
if (mChangeState.getChangedRuleType() == RULE_TYPE_MANUAL) {
if (!mChangeState.mFromSystemOrSystemUi
|| mChangeState.getNewManualRuleEnabler() == null) {
return;
}
packageName = mChangeState.getNewManualRuleEnabler();
userId = mChangeState.mNewConfig.user; // mNewConfig must not be null if enabler exists
}
// The conditions where we should consider reassigning UID for an automatic rule change:
// - we've determined it's not a user action
// - our current best guess is that the calling uid is system/sysui
if (mChangeState.getChangedRuleType() == RULE_TYPE_AUTOMATIC) {
if (mChangeState.getIsUserAction() || !mChangeState.mFromSystemOrSystemUi) {
return;
}
// Only try to get the package UID if there's exactly one changed automatic rule. If
// there's more than one that changes simultaneously, this is likely to be a boot and
// we can leave it attributed to system.
ArrayMap<String, ZenModeDiff.RuleDiff> changedRules =
mChangeState.getChangedAutomaticRules();
if (changedRules.size() != 1) {
return;
}
Pair<String, Integer> ruleInfo = mChangeState.getRulePackageAndUser(
changedRules.keyAt(0),
changedRules.valueAt(0));
if (ruleInfo == null || ruleInfo.first.equals(ZenModeConfig.SYSTEM_AUTHORITY)) {
// leave system rules as-is
return;
}
packageName = ruleInfo.first;
userId = ruleInfo.second;
}
if (userId == Process.INVALID_UID || packageName == null) {
// haven't found anything to look up.
return;
}
try {
int uid = mPm.getPackageUidAsUser(packageName, userId);
mChangeState.mCallingUid = uid;
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "unable to find package name " + packageName + " " + userId);
}
}
/**
* Actually log all changes stored in the current change state to statsd output. This method
* should not be used directly by callers; visible for override by subclasses.
*/
void logChanges() {
FrameworkStatsLog.write(FrameworkStatsLog.DND_STATE_CHANGED,
/* int32 event_id = 1 */ mChangeState.getEventId().getId(),
/* android.stats.dnd.ZenMode new_mode = 2 */ mChangeState.mNewZenMode,
/* android.stats.dnd.ZenMode previous_mode = 3 */ mChangeState.mPrevZenMode,
/* android.stats.dnd.RuleType rule_type = 4 */ mChangeState.getChangedRuleType(),
/* int32 num_rules_active = 5 */ mChangeState.getNumRulesActive(),
/* bool user_action = 6 */ mChangeState.getIsUserAction(),
/* int32 package_uid = 7 */ mChangeState.getPackageUid(),
/* DNDPolicyProto current_policy = 8 */ mChangeState.getDNDPolicyProto(),
/* bool are_channels_bypassing = 9 */ mChangeState.getAreChannelsBypassing());
}
/**
* Helper class for storing the set of information about a zen mode configuration at a specific
* time: the current zen mode setting, ZenModeConfig, and consolidated policy (a result of
* evaluating all active zen rules at the time).
*/
public static class ZenModeInfo {
final int mZenMode;
final ZenModeConfig mConfig;
final NotificationManager.Policy mPolicy;
ZenModeInfo(int zenMode, ZenModeConfig config, NotificationManager.Policy policy) {
mZenMode = zenMode;
// Store a copy of configs & policies to not accidentally pick up any further changes
mConfig = config != null ? config.copy() : null;
mPolicy = policy != null ? policy.copy() : null;
}
}
/**
* Class used to track overall changes in zen mode, since changes such as config updates happen
* in multiple stages (first changing the config, then re-evaluating zen mode and the
* consolidated policy), and which contains the logic of 1) whether to log the zen mode change
* and 2) deriving the properties to log.
*/
static class ZenStateChanges {
int mPrevZenMode = ZEN_MODE_UNKNOWN;
int mNewZenMode = ZEN_MODE_UNKNOWN;
ZenModeConfig mPrevConfig, mNewConfig;
NotificationManager.Policy mPrevPolicy, mNewPolicy;
int mCallingUid = Process.INVALID_UID;
boolean mFromSystemOrSystemUi = false;
private void init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
boolean fromSystemOrSystemUi) {
// previous & new may be the same -- that would indicate that zen mode hasn't changed.
mPrevZenMode = prevInfo.mZenMode;
mNewZenMode = newInfo.mZenMode;
mPrevConfig = prevInfo.mConfig;
mNewConfig = newInfo.mConfig;
mPrevPolicy = prevInfo.mPolicy;
mNewPolicy = newInfo.mPolicy;
mCallingUid = callingUid;
mFromSystemOrSystemUi = fromSystemOrSystemUi;
}
/**
* Returns whether there is a policy diff represented by this change. This doesn't count
* if the previous policy is null, as that would indicate having no information rather than
* having no previous policy.
*/
private boolean hasPolicyDiff() {
return mPrevPolicy != null && !Objects.equals(mPrevPolicy, mNewPolicy);
}
/**
* Whether the set of changes encapsulated in this state should be logged. This should only
* be called after methods to store config and zen mode info.
*/
private boolean shouldLogChanges() {
// Did zen mode change from off to on or vice versa? If so, log in all cases.
if (zenModeFlipped()) {
return true;
}
// If zen mode didn't change, did the policy or number of active rules change? We only
// care about changes that take effect while zen mode is on, so make sure the current
// zen mode is not "OFF"
if (mNewZenMode == ZEN_MODE_OFF) {
return false;
}
return hasPolicyDiff() || hasRuleCountDiff();
}
// Does the difference in zen mode go from off to on or vice versa?
private boolean zenModeFlipped() {
if (mPrevZenMode == mNewZenMode) {
return false;
}
// then it flipped if one or the other is off. (there's only one off state; there are
// multiple states one could consider "on")
return mPrevZenMode == ZEN_MODE_OFF || mNewZenMode == ZEN_MODE_OFF;
}
// Helper methods below to fill out the atom contents below:
/**
* Based on the changes, returns the event ID corresponding to the change. Assumes that
* shouldLogChanges() is true and already checked (and will Log.wtf if not true).
*/
ZenStateChangedEvent getEventId() {
if (!shouldLogChanges()) {
Log.wtf(TAG, "attempt to get DNDStateChanged fields without shouldLog=true");
}
if (zenModeFlipped()) {
if (mPrevZenMode == ZEN_MODE_OFF) {
return ZenStateChangedEvent.DND_TURNED_ON;
} else {
return ZenStateChangedEvent.DND_TURNED_OFF;
}
}
// zen mode didn't change; we must be here because of a policy change or rule change
if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
return ZenStateChangedEvent.DND_POLICY_CHANGED;
}
// Also no policy change, so it has to be a rule change
return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
}
/**
* Based on the config diff, determine which type of rule changed (or "unknown" to indicate
* unknown or neither).
* In the (probably somewhat unusual) case that there are both, manual takes precedence over
* automatic.
*/
int getChangedRuleType() {
ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
if (!diff.hasDiff()) {
// no diff in the config. this probably shouldn't happen, but we can consider it
// unknown (given that if zen mode changes it is usually accompanied by some rule
// turning on or off, which should cause a config diff).
return RULE_TYPE_UNKNOWN;
}
ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff();
if (manualDiff != null && manualDiff.hasDiff()) {
// a diff in the manual rule doesn't *necessarily* mean that it's responsible for
// the change -- only if it's been added or removed.
if (manualDiff.wasAdded() || manualDiff.wasRemoved()) {
return RULE_TYPE_MANUAL;
}
}
ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
if (autoDiffs != null) {
for (ZenModeDiff.RuleDiff d : autoDiffs.values()) {
if (d != null && d.hasDiff()) {
// If the rule became active or inactive, then this is probably relevant.
if (d.becameActive() || d.becameInactive()) {
return RULE_TYPE_AUTOMATIC;
}
}
}
}
return RULE_TYPE_UNKNOWN;
}
/**
* Returns whether the previous config and new config have a different number of active
* automatic or manual rules.
*/
private boolean hasRuleCountDiff() {
return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig);
}
/**
* Get the number of active rules represented in a zen mode config. Because this is based
* on a config, this does not take into account the zen mode at the time of the config,
* which means callers need to take the zen mode into account for whether the rules are
* actually active.
*/
int numActiveRulesInConfig(ZenModeConfig config) {
// If the config is null, return early
if (config == null) {
return 0;
}
int rules = 0;
// Loop through the config and check:
// - does a manual rule exist? (if it's non-null, it's active)
// - how many automatic rules are active, as defined by isAutomaticActive()?
if (config.manualRule != null) {
rules++;
}
if (config.automaticRules != null) {
for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
if (rule != null && rule.isAutomaticActive()) {
rules++;
}
}
}
return rules;
}
// Determine the number of (automatic & manual) rules active after the change takes place.
int getNumRulesActive() {
// If the zen mode has turned off, that means nothing can be active.
if (mNewZenMode == ZEN_MODE_OFF) {
return 0;
}
return numActiveRulesInConfig(mNewConfig);
}
/**
* Return our best guess as to whether the changes observed are due to a user action.
* Note that this won't be 100% accurate as we can't necessarily distinguish between a
* system uid call indicating "user interacted with Settings" vs "a system app changed
* something automatically".
*/
boolean getIsUserAction() {
// Approach:
// - if manual rule turned on or off, the calling UID is system, and the new manual
// rule does not have an enabler set, guess that this is likely to be a user action.
// This may represent a system app turning on DND automatically, but we guess "user"
// in this case.
// - note that this has a known failure mode of "manual rule turning off
// automatically after the default time runs out". We currently have no way
// of distinguishing this case from a user manually turning off the rule.
// - the reason for checking the enabler field is that a call may look like it's
// coming from a system UID, but if an enabler is set then the request came
// from an external source. "enabler" will be blank when manual rule is turned
// on from Quick Settings or Settings.
// - if an automatic rule's state changes in whether it is "enabled", then
// that is probably a user action.
// - if an automatic rule goes from "not snoozing" to "snoozing", that is probably
// a user action; that means that the user temporarily turned off DND associated
// with that rule.
// - if an automatic rule becomes active but does *not* change in its enabled state
// (covered by a previous case anyway), we guess that this is an automatic change.
// - if a rule is added or removed and the call comes from the system, we guess that
// this is a user action (as system rules can't be added or removed without a user
// action).
switch (getChangedRuleType()) {
case RULE_TYPE_MANUAL:
// TODO(b/278888961): Distinguish the automatically-turned-off state
return mFromSystemOrSystemUi && (getNewManualRuleEnabler() == null);
case RULE_TYPE_AUTOMATIC:
for (ZenModeDiff.RuleDiff d : getChangedAutomaticRules().values()) {
if (d.wasAdded() || d.wasRemoved()) {
// If the change comes from system, a rule being added/removed indicates
// a likely user action. From an app, it's harder to know for sure.
return mFromSystemOrSystemUi;
}
ZenModeDiff.FieldDiff enabled = d.getDiffForField(
ZenModeDiff.RuleDiff.FIELD_ENABLED);
if (enabled != null && enabled.hasDiff()) {
return true;
}
ZenModeDiff.FieldDiff snoozing = d.getDiffForField(
ZenModeDiff.RuleDiff.FIELD_SNOOZING);
if (snoozing != null && snoozing.hasDiff() && (boolean) snoozing.to()) {
return true;
}
}
// If the change was in an automatic rule and none of the "probably triggered
// by a user" cases apply, then it's probably an automatic change.
return false;
case RULE_TYPE_UNKNOWN:
default:
}
// If the change wasn't in a rule, but was in the zen policy: consider to be user action
// if the calling uid is system
if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
return mCallingUid == Process.SYSTEM_UID;
}
// don't know, or none of the other things triggered; assume not a user action
return false;
}
/**
* Get the package UID associated with this change, which is just the calling UID for the
* relevant method changes. This may get reset by ZenModeEventLogger, which has access to
* a PackageManager to get an appropriate UID for a package.
*/
int getPackageUid() {
return mCallingUid;
}
/**
* Convert the new policy to a DNDPolicyProto format for output in logs.
*/
byte[] getDNDPolicyProto() {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ProtoOutputStream proto = new ProtoOutputStream(bytes);
// While we don't expect this to be null at any point, guard against any weird cases.
if (mNewPolicy != null) {
proto.write(DNDPolicyProto.CALLS, toState(mNewPolicy.allowCalls()));
proto.write(DNDPolicyProto.REPEAT_CALLERS,
toState(mNewPolicy.allowRepeatCallers()));
proto.write(DNDPolicyProto.MESSAGES, toState(mNewPolicy.allowMessages()));
proto.write(DNDPolicyProto.CONVERSATIONS, toState(mNewPolicy.allowConversations()));
proto.write(DNDPolicyProto.REMINDERS, toState(mNewPolicy.allowReminders()));
proto.write(DNDPolicyProto.EVENTS, toState(mNewPolicy.allowEvents()));
proto.write(DNDPolicyProto.ALARMS, toState(mNewPolicy.allowAlarms()));
proto.write(DNDPolicyProto.MEDIA, toState(mNewPolicy.allowMedia()));
proto.write(DNDPolicyProto.SYSTEM, toState(mNewPolicy.allowSystem()));
proto.write(DNDPolicyProto.FULLSCREEN, toState(mNewPolicy.showFullScreenIntents()));
proto.write(DNDPolicyProto.LIGHTS, toState(mNewPolicy.showLights()));
proto.write(DNDPolicyProto.PEEK, toState(mNewPolicy.showPeeking()));
proto.write(DNDPolicyProto.STATUS_BAR, toState(mNewPolicy.showStatusBarIcons()));
proto.write(DNDPolicyProto.BADGE, toState(mNewPolicy.showBadges()));
proto.write(DNDPolicyProto.AMBIENT, toState(mNewPolicy.showAmbient()));
proto.write(DNDPolicyProto.NOTIFICATION_LIST,
toState(mNewPolicy.showInNotificationList()));
// Note: The DND policy proto uses the people type enum from *ZenPolicy* and not
// *NotificationManager.Policy* (which is the type of the consolidated policy).
// This applies to both call and message senders, but not conversation senders,
// where they use the same enum values.
proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
ZenModeConfig.getZenPolicySenders(mNewPolicy.allowCallsFrom()));
proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
ZenModeConfig.getZenPolicySenders(mNewPolicy.allowMessagesFrom()));
proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
mNewPolicy.allowConversationsFrom());
} else {
Log.wtf(TAG, "attempted to write zen mode log event with null policy");
}
proto.flush();
return bytes.toByteArray();
}
/**
* Get whether any channels are bypassing DND based on the current new policy.
*/
boolean getAreChannelsBypassing() {
if (mNewPolicy != null) {
return (mNewPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0;
}
return false;
}
private boolean hasChannelsBypassingDiff() {
boolean prevChannelsBypassing = mPrevPolicy != null
? (mPrevPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0 : false;
return prevChannelsBypassing != getAreChannelsBypassing();
}
/**
* helper method to turn a boolean allow or disallow state into STATE_ALLOW or
* STATE_DISALLOW (there is no concept of "unset" in NM.Policy.)
*/
private int toState(boolean allow) {
return allow ? ZenPolicy.STATE_ALLOW : ZenPolicy.STATE_DISALLOW;
}
/**
* Get the list of automatic rules that have any diff (as a List of ZenModeDiff.RuleDiff).
* Returns an empty list if there isn't anything.
*/
private @NonNull ArrayMap<String, ZenModeDiff.RuleDiff> getChangedAutomaticRules() {
ArrayMap<String, ZenModeDiff.RuleDiff> ruleDiffs = new ArrayMap<>();
ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
if (!diff.hasDiff()) {
return ruleDiffs;
}
ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
if (autoDiffs != null) {
return autoDiffs;
}
return ruleDiffs;
}
/**
* Get the package name associated with this rule's owner, given its id and associated
* RuleDiff, as well as the user ID associated with the config it was found in. Returns null
* if none could be found.
*/
private Pair<String, Integer> getRulePackageAndUser(String id, ZenModeDiff.RuleDiff diff) {
// look for the rule info in the new config unless the rule was deleted.
ZenModeConfig configForSearch = mNewConfig;
if (diff.wasRemoved()) {
configForSearch = mPrevConfig;
}
if (configForSearch == null) {
return null;
}
ZenModeConfig.ZenRule rule = configForSearch.automaticRules.getOrDefault(id, null);
if (rule != null) {
if (rule.component != null) {
return new Pair(rule.component.getPackageName(), configForSearch.user);
}
if (rule.configurationActivity != null) {
return new Pair(rule.configurationActivity.getPackageName(),
configForSearch.user);
}
}
return null;
}
/**
* Get the package name listed as the manual rule "enabler", if it exists in the new config.
*/
private String getNewManualRuleEnabler() {
if (mNewConfig == null || mNewConfig.manualRule == null) {
return null;
}
return mNewConfig.manualRule.enabler;
}
/**
* Makes a copy for storing intermediate state for testing purposes.
*/
protected ZenStateChanges copy() {
ZenStateChanges copy = new ZenStateChanges();
copy.mPrevZenMode = mPrevZenMode;
copy.mNewZenMode = mNewZenMode;
copy.mPrevConfig = mPrevConfig.copy();
copy.mNewConfig = mNewConfig.copy();
copy.mPrevPolicy = mPrevPolicy.copy();
copy.mNewPolicy = mNewPolicy.copy();
copy.mCallingUid = mCallingUid;
copy.mFromSystemOrSystemUi = mFromSystemOrSystemUi;
return copy;
}
}
}

View File

@@ -76,6 +76,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
@@ -125,6 +126,8 @@ public class ZenModeHelper {
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
@VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
@@ -144,7 +147,9 @@ public class ZenModeHelper {
private String[] mPriorityOnlyDndExemptPackages;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
SystemUiSystemPropertiesFlags.FlagResolver flagResolver,
ZenModeEventLogger zenModeEventLogger) {
mContext = context;
mHandler = new H(looper);
addCallback(mMetrics);
@@ -165,6 +170,8 @@ public class ZenModeHelper {
mConditions = new ZenModeConditions(this, conditionProviders);
mServiceConfig = conditionProviders.getConfig();
mStatsEventBuilderFactory = statsEventBuilderFactory;
mFlagResolver = flagResolver;
mZenModeEventLogger = zenModeEventLogger;
}
public Looper getLooper() {
@@ -214,7 +221,13 @@ public class ZenModeHelper {
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
evaluateZenMode("init", true /*setRingerMode*/);
synchronized (mConfig) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
updateConfigAndZenModeLocked(mConfig, "init", true /*setRingerMode*/,
Process.SYSTEM_UID /* callingUid */, true /* is system */);
}
}
public void onSystemReady() {
@@ -266,7 +279,7 @@ public class ZenModeHelper {
config.user = user;
}
synchronized (mConfig) {
setConfigLocked(config, null, reason);
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
}
@@ -275,11 +288,13 @@ public class ZenModeHelper {
return NotificationManager.zenModeToInterruptionFilter(mZenMode);
}
public void requestFromListener(ComponentName name, int filter) {
public void requestFromListener(ComponentName name, int filter, int callingUid,
boolean fromSystemOrSystemUi) {
final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
if (newZen != -1) {
setManualZenMode(newZen, null, name != null ? name.getPackageName() : null,
"listener:" + (name != null ? name.flattenToShortString() : null));
"listener:" + (name != null ? name.flattenToShortString() : null),
callingUid, fromSystemOrSystemUi);
}
}
@@ -324,7 +339,7 @@ public class ZenModeHelper {
}
public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
String reason) {
String reason, int callingUid, boolean fromSystemOrSystemUi) {
if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
@@ -360,7 +375,8 @@ public class ZenModeHelper {
ZenRule rule = new ZenRule();
populateZenRule(pkg, automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
if (setConfigLocked(newConfig, reason, rule.component, true, callingUid,
fromSystemOrSystemUi)) {
return rule.id;
} else {
throw new AndroidRuntimeException("Could not create rule");
@@ -369,7 +385,7 @@ public class ZenModeHelper {
}
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason) {
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return false;
@@ -395,11 +411,13 @@ public class ZenModeHelper {
}
populateZenRule(rule.pkg, automaticZenRule, rule, false);
return setConfigLocked(newConfig, reason, rule.component, true);
return setConfigLocked(newConfig, reason, rule.component, true, callingUid,
fromSystemOrSystemUi);
}
}
public boolean removeAutomaticZenRule(String id, String reason) {
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return false;
@@ -424,11 +442,13 @@ public class ZenModeHelper {
}
dispatchOnAutomaticRuleStatusChanged(
mConfig.user, ruleToRemove.getPkg(), id, AUTOMATIC_RULE_STATUS_REMOVED);
return setConfigLocked(newConfig, reason, null, true);
return setConfigLocked(newConfig, reason, null, true, callingUid,
fromSystemOrSystemUi);
}
}
public boolean removeAutomaticZenRules(String packageName, String reason) {
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return false;
@@ -439,11 +459,13 @@ public class ZenModeHelper {
newConfig.automaticRules.removeAt(i);
}
}
return setConfigLocked(newConfig, reason, null, true);
return setConfigLocked(newConfig, reason, null, true, callingUid,
fromSystemOrSystemUi);
}
}
public void setAutomaticZenRuleState(String id, Condition condition) {
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return;
@@ -451,11 +473,13 @@ public class ZenModeHelper {
newConfig = mConfig.copy();
ArrayList<ZenRule> rules = new ArrayList<>();
rules.add(newConfig.automaticRules.get(id));
setAutomaticZenRuleStateLocked(newConfig, rules, condition);
setAutomaticZenRuleStateLocked(newConfig, rules, condition, callingUid,
fromSystemOrSystemUi);
}
}
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition) {
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return;
@@ -463,18 +487,19 @@ public class ZenModeHelper {
setAutomaticZenRuleStateLocked(newConfig,
findMatchingRules(newConfig, ruleDefinition, condition),
condition);
condition, callingUid, fromSystemOrSystemUi);
}
}
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition) {
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
for (ZenRule rule : rules) {
rule.condition = condition;
updateSnoozing(rule);
setConfigLocked(config, rule.component, "conditionChanged");
setConfigLocked(config, rule.component, "conditionChanged", callingUid,
fromSystemOrSystemUi);
}
}
@@ -561,7 +586,7 @@ public class ZenModeHelper {
}
}
protected void updateDefaultZenRules() {
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
@@ -575,7 +600,7 @@ public class ZenModeHelper {
// update default rule (if locale changed, name of rule will change)
currRule.name = defaultRule.name;
updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
"locale changed");
"locale changed", callingUid, fromSystemOrSystemUi);
}
}
}
@@ -650,14 +675,16 @@ public class ZenModeHelper {
return azr;
}
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/);
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
setManualZenMode(zenMode, conditionId, reason, caller, true /*setRingerMode*/, callingUid,
fromSystemOrSystemUi);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, 0);
}
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode) {
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return;
@@ -681,7 +708,8 @@ public class ZenModeHelper {
newRule.enabler = caller;
newConfig.manualRule = newRule;
}
setConfigLocked(newConfig, reason, null, setRingerMode);
setConfigLocked(newConfig, reason, null, setRingerMode, callingUid,
fromSystemOrSystemUi);
}
}
@@ -806,7 +834,7 @@ public class ZenModeHelper {
}
if (DEBUG) Log.d(TAG, reason);
synchronized (mConfig) {
setConfigLocked(config, null, reason);
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
}
@@ -838,12 +866,13 @@ public class ZenModeHelper {
/**
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy) {
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
if (policy == null || mConfig == null) return;
synchronized (mConfig) {
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy");
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
fromSystemOrSystemUi);
}
}
@@ -868,7 +897,8 @@ public class ZenModeHelper {
}
}
}
setConfigLocked(newConfig, null, "cleanUpZenRules");
setConfigLocked(newConfig, null, "cleanUpZenRules", Process.SYSTEM_UID,
true);
}
}
@@ -889,18 +919,21 @@ public class ZenModeHelper {
}
public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/);
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
}
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason) {
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
synchronized (mConfig) {
setConfigLocked(config, triggeringComponent, reason);
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode) {
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
final long identity = Binder.clearCallingIdentity();
try {
if (config == null || !config.isValid()) {
@@ -927,17 +960,11 @@ public class ZenModeHelper {
// send some broadcasts
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
if (!config.equals(mConfig)) {
mConfig = config;
dispatchOnConfigChanged();
updateConsolidatedPolicy(reason);
}
if (policyChanged) {
dispatchOnPolicyChanged();
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
evaluateZenMode(reason, setRingerMode);
updateConfigAndZenModeLocked(config, reason, setRingerMode, callingUid,
fromSystemOrSystemUi);
mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/);
return true;
} catch (SecurityException e) {
@@ -948,6 +975,34 @@ public class ZenModeHelper {
}
}
/**
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS);
// Store (a copy of) all config and zen mode info prior to any changes taking effect
ZenModeEventLogger.ZenModeInfo prevInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
if (!config.equals(mConfig)) {
mConfig = config;
dispatchOnConfigChanged();
updateConsolidatedPolicy(reason);
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
evaluateZenMode(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
mZenMode, mConfig, mConsolidatedPolicy);
mZenModeEventLogger.maybeLogZenChange(prevInfo, newInfo, callingUid,
fromSystemOrSystemUi);
}
}
private int getZenModeSetting() {
return Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF);
}
@@ -1366,7 +1421,7 @@ public class ZenModeHelper {
if (newZen != -1) {
setManualZenMode(newZen, null, "ringerModeInternal", null,
false /*setRingerMode*/);
false /*setRingerMode*/, Process.SYSTEM_UID, true);
}
if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
@@ -1404,7 +1459,7 @@ public class ZenModeHelper {
}
if (newZen != -1) {
setManualZenMode(newZen, null, "ringerModeExternal", caller,
false /*setRingerMode*/);
false /*setRingerMode*/, Process.SYSTEM_UID, true);
}
ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller,

View File

@@ -2194,6 +2194,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), anyString(), anyInt(), anyString(), anyInt()))
.thenReturn(SHOW_IMMEDIATELY);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2220,6 +2221,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), anyString(), anyInt(), anyString(), anyInt()))
.thenReturn(SHOW_IMMEDIATELY);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2243,6 +2245,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearChild()
throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2266,6 +2269,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCancelNotificationWithTag_fromApp_canCancelOngoingNoClearParent()
throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
@@ -2292,6 +2296,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), anyString(), anyInt(), anyString(), anyInt()))
.thenReturn(SHOW_IMMEDIATELY);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2320,6 +2325,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), anyString(), anyInt(), anyString(), anyInt()))
.thenReturn(SHOW_IMMEDIATELY);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2345,6 +2351,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearChild()
throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -2370,6 +2377,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCancelAllNotifications_fromApp_canCancelOngoingNoClearParent()
throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
@@ -3209,7 +3217,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
eq(channel2.getId()), anyBoolean()))
.thenReturn(channel2);
when(mPreferencesHelper.createNotificationChannel(eq(PKG), anyInt(),
eq(channel2), anyBoolean(), anyBoolean()))
eq(channel2), anyBoolean(), anyBoolean(), anyInt(), anyBoolean()))
.thenReturn(true);
reset(mListeners);
@@ -3268,7 +3276,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
when(mPreferencesHelper.deleteNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()))).thenReturn(true);
eq(mTestNotificationChannel.getId()), anyInt(), anyBoolean())).thenReturn(true);
reset(mListeners);
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
@@ -3367,7 +3375,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
null, PKG, Process.myUserHandle(), mTestNotificationChannel);
verify(mPreferencesHelper, times(1)).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -3389,7 +3397,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
verify(mPreferencesHelper, never()).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -3415,7 +3423,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
verify(mPreferencesHelper, never()).updateNotificationChannel(
anyString(), anyInt(), any(), anyBoolean());
anyString(), anyInt(), any(), anyBoolean(), anyInt(), anyBoolean());
verify(mListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
@@ -4895,7 +4903,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mLocaleChangeReceiver.onReceive(mContext,
new Intent(Intent.ACTION_LOCALE_CHANGED));
verify(mZenModeHelper, times(1)).updateDefaultZenRules();
verify(mZenModeHelper, times(1)).updateDefaultZenRules(
anyInt(), anyBoolean());
}
@Test
@@ -6759,6 +6768,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6781,6 +6791,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6800,6 +6811,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6829,6 +6841,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6852,6 +6865,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6886,6 +6900,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6905,6 +6920,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6924,6 +6940,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -6954,6 +6971,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
setAppInForegroundForToasts(mUid, false);
@@ -6976,6 +6994,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
setAppInForegroundForToasts(mUid, true);
@@ -6997,6 +7016,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);
setAppInForegroundForToasts(mUid, false);
@@ -7018,6 +7038,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
setAppInForegroundForToasts(mUid, false);
@@ -7076,6 +7097,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -7097,6 +7119,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -7200,6 +7223,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
mockIsUserVisible(DEFAULT_DISPLAY, false);
@@ -7220,6 +7244,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -7242,6 +7267,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -7286,6 +7312,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
@@ -8177,7 +8204,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.addAutomaticZenRule(rule, "com.android.settings");
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
anyInt(), eq(true)); // system call counts as "is system or system ui"
}
@Test
@@ -8198,7 +8226,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.addAutomaticZenRule(rule, "com.android.settings");
// verify that zen mode helper gets passed in a package name of "android"
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString(),
anyInt(), eq(true)); // system call counts as "system or system ui"
}
@Test
@@ -8218,7 +8247,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// verify that zen mode helper gets passed in the package name from the arg, not the owner
verify(mockZenModeHelper).addAutomaticZenRule(
eq("another.package"), eq(rule), anyString());
eq("another.package"), eq(rule), anyString(), anyInt(),
eq(false)); // doesn't count as a system/systemui call
}
@Test
@@ -10037,8 +10067,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
// set the testable NMS to not system uid
// set the testable NMS to not system uid/appid
mService.isSystemUid = false;
mService.isSystemAppId = false;
// make sure a caller without listener access or read_contacts permission can't call
// matchesCallFilter.
@@ -10077,6 +10108,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMatchesCallFilter_hasListenerPermission() throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
// make sure a caller with only listener access and not read_contacts permission can call
// matchesCallFilter.
@@ -10095,6 +10127,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testMatchesCallFilter_hasContactsPermission() throws Exception {
mService.isSystemUid = false;
mService.isSystemAppId = false;
// make sure a caller with only read_contacts permission and not listener access can call
// matchesCallFilter.
@@ -11225,6 +11258,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mJsi.isNotificationAssociatedWithAnyUserInitiatedJobs(anyInt(), anyInt(), anyString()))
.thenReturn(true);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -11249,6 +11283,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mJsi.isNotificationAssociatedWithAnyUserInitiatedJobs(anyInt(), anyInt(), anyString()))
.thenReturn(true);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_USER_INITIATED_JOB;
@@ -11273,6 +11308,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mJsi.isNotificationAssociatedWithAnyUserInitiatedJobs(anyInt(), anyInt(), anyString()))
.thenReturn(true);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
@@ -11299,6 +11335,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mJsi.isNotificationAssociatedWithAnyUserInitiatedJobs(anyInt(), anyInt(), anyString()))
.thenReturn(true);
mService.isSystemUid = false;
mService.isSystemAppId = false;
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
parent.getNotification().flags |= FLAG_USER_INITIATED_JOB;
@@ -11722,6 +11759,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private void allowTestPackageToToast() throws Exception {
assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
mService.isSystemUid = false;
mService.isSystemAppId = false;
setToastRateIsWithinQuota(true);
setIfPackageHasPermissionToAvoidToastRateLimiting(TEST_PACKAGE, false);
// package is not suspended

View File

@@ -67,7 +67,13 @@ public class TestableNotificationManagerService extends NotificationManagerServi
@Override
protected boolean isCallerSystemOrPhone() {
countSystemChecks++;
return isSystemUid;
return isSystemUid || isSystemAppId;
}
@Override
protected boolean isCallerIsSystemOrSystemUi() {
countSystemChecks++;
return isSystemUid || isSystemAppId;
}
@Override

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2023 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;
import android.content.pm.PackageManager;
import com.android.os.dnd.DNDPolicyProto;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.ArrayList;
import java.util.List;
/**
* ZenModeEventLoggerFake extends ZenModeEventLogger for ease of verifying logging output. This
* class behaves exactly the same as its parent class except that instead of actually logging, it
* stores the full information at time of log whenever something would be logged.
*/
public class ZenModeEventLoggerFake extends ZenModeEventLogger {
// A record of the contents of each event we'd log, stored by recording the ChangeState object
// at the time of the log.
private List<ZenStateChanges> mChanges = new ArrayList<>();
public ZenModeEventLoggerFake(PackageManager pm) {
super(pm);
}
@Override
void logChanges() {
// current change state being logged
mChanges.add(mChangeState.copy());
}
// Reset the state of the logger (remove all changes).
public void reset() {
mChanges = new ArrayList<>();
}
// Returns the number of changes logged.
public int numLoggedChanges() {
return mChanges.size();
}
// is index i out of range for the set of changes we have
private boolean outOfRange(int i) {
return i < 0 || i >= mChanges.size();
}
// Throw an exception if provided index is out of range
private void checkInRange(int i) throws IllegalArgumentException {
if (outOfRange(i)) {
throw new IllegalArgumentException("invalid index for logged event: " + i);
}
}
// Get the UiEvent ID of the i'th logged event.
public int getEventId(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getEventId().getId();
}
// Get the previous zen mode associated with the change at event i.
public int getPrevZenMode(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).mPrevZenMode;
}
// Get the new zen mode associated with the change at event i.
public int getNewZenMode(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).mNewZenMode;
}
// Get the changed rule type associated with event i.
public int getChangedRuleType(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getChangedRuleType();
}
public int getNumRulesActive(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getNumRulesActive();
}
public boolean getFromSystemOrSystemUi(int i) throws IllegalArgumentException {
// While this isn't a logged output value, it's still helpful to check in tests.
checkInRange(i);
return mChanges.get(i).mFromSystemOrSystemUi;
}
public boolean getIsUserAction(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getIsUserAction();
}
public int getPackageUid(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getPackageUid();
}
// Get the DNDPolicyProto (unmarshaled from bytes) associated with event i.
// Note that in creation of the log, we use a notification.proto mirror of DNDPolicyProto,
// but here we use the actual logging-side proto to make sure they continue to match.
public DNDPolicyProto getPolicyProto(int i) throws IllegalArgumentException {
checkInRange(i);
byte[] policyBytes = mChanges.get(i).getDNDPolicyProto();
try {
return DNDPolicyProto.parseFrom(policyBytes);
} catch (InvalidProtocolBufferException e) {
return null; // couldn't turn it into proto
}
}
public boolean getAreChannelsBypassing(int i) throws IllegalArgumentException {
checkInRange(i);
return mChanges.get(i).getAreChannelsBypassing();
}
}

View File

@@ -34,16 +34,21 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.os.dnd.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
import static com.android.os.dnd.DNDModeProto.ENABLED_FIELD_NUMBER;
import static com.android.os.dnd.DNDModeProto.ID_FIELD_NUMBER;
import static com.android.os.dnd.DNDModeProto.UID_FIELD_NUMBER;
import static com.android.os.dnd.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
import static junit.framework.Assert.assertEquals;
@@ -104,9 +109,12 @@ import android.util.StatsEvent;
import android.util.Xml;
import com.android.internal.R;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.os.dnd.DNDPolicyProto;
import com.android.os.dnd.DNDProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.ManagedServices.UserProfiles;
@@ -152,6 +160,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
private ContentResolver mContentResolver;
@Mock AppOpsManager mAppOps;
private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
ZenModeEventLoggerFake mZenModeEventLogger;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -176,8 +186,10 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AppGlobals.getPackageManager());
mConditionProviders.addSystemProvider(new CountdownConditionProvider());
mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(),
mConditionProviders, mStatsEventBuilderFactory);
mConditionProviders, mStatsEventBuilderFactory, mTestFlagResolver,
mZenModeEventLogger);
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
@@ -188,6 +200,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[] {pkg});
mZenModeHelper.mPm = mPackageManager;
mZenModeEventLogger.reset();
}
private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
@@ -224,7 +238,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL);
serializer.endDocument();
serializer.flush();
mZenModeHelper.setConfig(new ZenModeConfig(), null, "writing xml");
mZenModeHelper.setConfig(new ZenModeConfig(), null, "writing xml", Process.SYSTEM_UID,
true);
return baos;
}
@@ -239,7 +254,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
serializer.flush();
ZenModeConfig newConfig = new ZenModeConfig();
newConfig.user = userId;
mZenModeHelper.setConfig(newConfig, null, "writing xml");
mZenModeHelper.setConfig(newConfig, null, "writing xml", Process.SYSTEM_UID, true);
return baos;
}
@@ -822,14 +837,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mAudioManager = mAudioManager;
setupZenConfig();
// Change the config a little bit, but enough that it would turn zen mode on
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.manualRule = new ZenModeConfig.ZenRule();
newConfig.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
newConfig.manualRule.enabled = true;
mZenModeHelper.setConfig(newConfig, null, "test");
// Turn manual zen mode on
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null,
"test", CUSTOM_PKG_UID, false);
// audio manager shouldn't do anything until the handler processes its messagse
// audio manager shouldn't do anything until the handler processes its messages
verify(mAudioManager, never()).updateRingerModeAffectedStreamsInternal();
// now process the looper's messages
@@ -993,7 +1005,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, "test");
mZenModeHelper.removeAutomaticZenRule(CUSTOM_RULE_ID, "test", CUSTOM_PKG_UID, false);
assertTrue(-1
== mZenModeHelper.mRulesUidCache.getOrDefault(CUSTOM_PKG_NAME + "|" + 0, -1));
}
@@ -1044,12 +1056,12 @@ public class ZenModeHelperTest extends UiServiceTestCase {
config10.user = 10;
config10.allowAlarms = true;
config10.allowMedia = true;
mZenModeHelper.setConfig(config10, null, "writeXml");
mZenModeHelper.setConfig(config10, null, "writeXml", Process.SYSTEM_UID, true);
ZenModeConfig config11 = mZenModeHelper.mConfig.copy();
config11.user = 11;
config11.allowAlarms = false;
config11.allowMedia = false;
mZenModeHelper.setConfig(config11, null, "writeXml");
mZenModeHelper.setConfig(config11, null, "writeXml", Process.SYSTEM_UID, true);
// Backup user 10 and reset values.
ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10);
@@ -1552,7 +1564,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig config = new ZenModeConfig();
config.automaticRules = new ArrayMap<>();
mZenModeHelper.mConfig = config;
mZenModeHelper.updateDefaultZenRules(); // shouldn't throw null pointer
mZenModeHelper.updateDefaultZenRules(
Process.SYSTEM_UID, true); // shouldn't throw null pointer
mZenModeHelper.pullRules(events); // shouldn't throw null pointer
}
@@ -1577,7 +1590,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
mZenModeHelper.updateDefaultZenRules();
mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
assertEquals(updatedDefaultRule,
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
}
@@ -1603,7 +1616,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
mZenModeHelper.updateDefaultZenRules();
mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
assertEquals(updatedDefaultRule,
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
}
@@ -1630,7 +1643,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
autoRules.put(SCHEDULE_DEFAULT_RULE_ID, customDefaultRule);
mZenModeHelper.mConfig.automaticRules = autoRules;
mZenModeHelper.updateDefaultZenRules();
mZenModeHelper.updateDefaultZenRules(Process.SYSTEM_UID, true);
ZenModeConfig.ZenRule ruleAfterUpdating =
mZenModeHelper.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
@@ -1653,7 +1666,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
// We need the package name to be something that's not "android" so there aren't any
// existing rules under that package.
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
assertNotNull(id);
}
try {
@@ -1663,7 +1677,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1683,7 +1698,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
assertNotNull(id);
}
try {
@@ -1693,7 +1709,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1713,7 +1730,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
assertNotNull(id);
}
try {
@@ -1723,7 +1741,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("pkgname", zenRule, "test",
CUSTOM_PKG_UID, false);
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
@@ -1738,7 +1757,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
Process.SYSTEM_UID, true);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1758,7 +1778,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
Process.SYSTEM_UID, true);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
@@ -1781,9 +1802,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
CUSTOM_PKG_UID, false);
mZenModeHelper.setAutomaticZenRuleState(zenRule.getConditionId(),
new Condition(zenRule.getConditionId(), "", STATE_TRUE));
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
CUSTOM_PKG_UID, false);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals(STATE_TRUE, ruleInConfig.condition.state);
@@ -1798,7 +1821,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
CUSTOM_PKG_UID, false);
AutomaticZenRule zenRule2 = new AutomaticZenRule("NEW",
null,
@@ -1807,7 +1831,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "");
mZenModeHelper.updateAutomaticZenRule(id, zenRule2, "", CUSTOM_PKG_UID, false);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertEquals("NEW", ruleInConfig.name);
@@ -1822,14 +1846,15 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
CUSTOM_PKG_UID, false);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.getName(), ruleInConfig.name);
mZenModeHelper.removeAutomaticZenRule(id, "test");
mZenModeHelper.removeAutomaticZenRule(id, "test", CUSTOM_PKG_UID, false);
assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
}
@@ -1841,14 +1866,16 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule(null, zenRule, "test",
CUSTOM_PKG_UID, false);
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
assertTrue(ruleInConfig != null);
assertEquals(zenRule.getName(), ruleInConfig.name);
mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), "test");
mZenModeHelper.removeAutomaticZenRules(mContext.getPackageName(), "test",
CUSTOM_PKG_UID, false);
assertNull(mZenModeHelper.mConfig.automaticRules.get(id));
}
@@ -1863,15 +1890,17 @@ public class ZenModeHelperTest extends UiServiceTestCase {
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test");
String id = mZenModeHelper.addAutomaticZenRule("android", zenRule, "test",
Process.SYSTEM_UID, true);
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test");
String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2, "test",
Process.SYSTEM_UID, true);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition);
mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
@@ -1884,17 +1913,17 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
}
condition = new Condition(sharedUri, "", Condition.STATE_FALSE);
mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition);
condition = new Condition(sharedUri, "", STATE_FALSE);
mZenModeHelper.setAutomaticZenRuleState(sharedUri, condition, Process.SYSTEM_UID, true);
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
assertNotNull(rule.condition);
assertTrue(rule.condition.state == Condition.STATE_FALSE);
assertTrue(rule.condition.state == STATE_FALSE);
}
if (rule.id.equals(id2)) {
assertNotNull(rule.condition);
assertTrue(rule.condition.state == Condition.STATE_FALSE);
assertTrue(rule.condition.state == STATE_FALSE);
}
}
}
@@ -1904,16 +1933,512 @@ public class ZenModeHelperTest extends UiServiceTestCase {
setupZenConfig();
// note that caller=null because that's how it comes in from NMS.setZenMode
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "");
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
Process.SYSTEM_UID, true);
// confirm that setting zen mode via setManualZenMode changed the zen mode correctly
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
// and also that it works to turn it back off again
mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "");
mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
@Test
public void testZenModeEventLog_setManualZenMode() throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
Process.SYSTEM_UID, true);
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
false);
// In total, this should be 2 loggable changes
assertEquals(2, mZenModeEventLogger.numLoggedChanges());
// we expect the following changes from turning zen mode on:
// - manual rule added
// - zen mode -> ZEN_MODE_IMPORTANT_INTERRUPTIONS
// This should combine to 1 log event (zen mode turns on) with the following properties:
// - event ID: DND_TURNED_ON
// - new zen mode = important interruptions; prev zen mode = off
// - changed rule type = manual
// - rules active = 1
// - user action = true (system-based turning zen mode on)
// - package uid = system (as set above)
// - resulting DNDPolicyProto the same as the values in setupZenConfig()
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertTrue(mZenModeEventLogger.getFromSystemOrSystemUi(0));
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
// and from turning zen mode off:
// - event ID: DND_TURNED_OFF
// - new zen mode = off; previous = important interruptions
// - changed rule type = manual
// - rules active = 0
// - user action = false
// - package uid = custom one passed in above
// - DNDPolicyProto still the same
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
}
@Test
public void testZenModeEventLog_automaticRules() throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Add a new automatic zen rule that's enabled
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule,
"test", Process.SYSTEM_UID, true);
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
// Add a new system rule
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
null,
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
"test", Process.SYSTEM_UID, true);
// Event 3: turn on the system rule
mZenModeHelper.setAutomaticZenRuleState(systemId,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Event 4: "User" deletes the rule
mZenModeHelper.removeAutomaticZenRule(systemId, "", Process.SYSTEM_UID, true);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
// We should see an event from the automatic rule turning on; it should have the following
// properties:
// - event ID: DND_TURNED_ON
// - zen mode: OFF -> IMPORTANT_INTERRUPTIONS
// - automatic rule change
// - 1 rule (newly) active
// - automatic (is not a user action)
// - package UID is written to be the rule *owner* even though it "comes from system"
// - zen policy is the same as the set-up zen config
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
// When the automatic rule is disabled, this should turn off zen mode and also count as a
// user action.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
// When the system rule is enabled, this counts as an automatic action that comes from the
// system and turns on DND
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(2));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(2));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(2));
assertFalse(mZenModeEventLogger.getIsUserAction(2));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
// When the system rule is deleted, we consider this a user action that turns DND off
// (again)
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(3));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(3));
assertEquals(0, mZenModeEventLogger.getNumRulesActive(3));
assertTrue(mZenModeEventLogger.getIsUserAction(3));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(3));
}
@Test
public void testZenModeEventLog_policyChanges() throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// First just turn zen mode on
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
Process.SYSTEM_UID, true);
// Now change the policy slightly; want to confirm that this'll be reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.allowAlarms = true;
newConfig.allowRepeatCallers = false;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
true);
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
Process.SYSTEM_UID, true);
// Change the policy again
newConfig.allowMessages = false;
newConfig.allowRepeatCallers = true;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
true);
// Total events: we only expect ones for turning on, changing policy, and turning off
assertEquals(3, mZenModeEventLogger.numLoggedChanges());
// The first event is just turning DND on; make sure the policy is what we expect there
// before it changes in the next stage
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
// Second message where we change the policy:
// - DND_POLICY_CHANGED (indicates only the policy changed and nothing else)
// - rule type: unknown (it's a policy change, not a rule change)
// - user action (because it comes from a "system" uid)
// - check the specific things changed above
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(DNDProtoEnums.UNKNOWN_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertTrue(mZenModeEventLogger.getIsUserAction(1));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
DNDPolicyProto dndProto = mZenModeEventLogger.getPolicyProto(1);
assertEquals(STATE_ALLOW, dndProto.getAlarms().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getRepeatCallers().getNumber());
// The third and final event should turn DND off
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(2));
// There should be no fourth event for changing the policy the second time.
}
@Test
public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
Process.SYSTEM_UID, true);
// Rule 2, same as rule 1
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
null,
new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
Process.SYSTEM_UID, true);
// Rule 3, has stricter settings than the default settings
ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
ruleConfig.allowReminders = false;
ruleConfig.allowCalls = false;
ruleConfig.allowMessages = false;
AutomaticZenRule zenRule3 = new AutomaticZenRule("name3",
null,
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
ruleConfig.toZenPolicy(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, "test",
Process.SYSTEM_UID, true);
// First: turn on rule 1
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Second: turn on rule 2
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Third: turn on rule 3
mZenModeHelper.setAutomaticZenRuleState(id3,
new Condition(zenRule3.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Fourth: Turn *off* rule 2
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
Process.SYSTEM_UID, true);
// This should result in a total of four events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
// Event 1: rule 1 turns on. We expect this to turn on DND (zen mode) overall, so that's
// what the event should reflect. At this time, the policy is the same as initial setup.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
// Event 2: rule 2 turns on. This should not change anything about the policy, so the only
// change is that there are more rules active now.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(2, mZenModeEventLogger.getNumRulesActive(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(1));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(1));
// Event 3: rule 3 turns on. This should trigger a policy change, and be classified as such,
// but meanwhile also change the number of active rules.
// Rule 3 is also set up to be a "system"-owned rule, so the caller UID should remain system
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId(),
mZenModeEventLogger.getEventId(2));
assertEquals(3, mZenModeEventLogger.getNumRulesActive(2));
assertFalse(mZenModeEventLogger.getIsUserAction(2));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
DNDPolicyProto dndProto = mZenModeEventLogger.getPolicyProto(2);
assertEquals(STATE_DISALLOW, dndProto.getReminders().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getCalls().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getMessages().getNumber());
// Event 4: rule 2 turns off. Because rule 3 is still on and stricter than rule 1 (also
// still on), there should be no policy change as a result of rule 2 going away. Therefore
// this event should again only be an active rule change.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(3));
assertEquals(2, mZenModeEventLogger.getNumRulesActive(3));
assertFalse(mZenModeEventLogger.getIsUserAction(3));
}
@Test
public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException {
// If evaluateZenMode is called independently of a config change, don't log.
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelper.evaluateZenMode("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
// but still, nothing should've been logged
assertEquals(0, mZenModeEventLogger.numLoggedChanges());
}
@Test
public void testZenModeEventLog_reassignUid() throws IllegalArgumentException {
// Test that, only in specific cases, we reassign the calling UID to one associated with
// the automatic rule owner.
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Rule 1, owned by a package
AutomaticZenRule zenRule = new AutomaticZenRule("name",
null,
new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
Process.SYSTEM_UID, true);
// Rule 2, same as rule 1 but owned by the system
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
null,
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, "test",
Process.SYSTEM_UID, true);
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
// re-evaluated to the one associat.d with CUSTOM_PKG_NAME.
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Second: turn on rule 2. This is a system-owned rule and the UID should not be modified
// (nor even looked up; the mock PackageManager won't handle "android" as input).
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
Process.SYSTEM_UID, true);
// Disable rule 1. Because this looks like a user action, the UID should not be modified
// from the system-provided one.
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule, "", Process.SYSTEM_UID, true);
// Add a manual rule. Any manual rule changes should not get calling uids reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
CUSTOM_PKG_UID, false);
// Change rule 2's condition, but from some other UID. Since it doesn't look like it's from
// the system, we keep the UID info.
mZenModeHelper.setAutomaticZenRuleState(id2,
new Condition(zenRule2.getConditionId(), "", STATE_FALSE),
12345, false);
// That was 5 events total
assertEquals(5, mZenModeEventLogger.numLoggedChanges());
// The first event (activating rule 1) should be of type "zen mode turns on", automatic,
// have a package UID of CUSTOM_PKG_UID
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
// The second event (activating rule 2) should have similar other properties but the UID
// should be system.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(1));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
assertFalse(mZenModeEventLogger.getIsUserAction(1));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(1));
// Third event: disable rule 1. This looks like a user action so UID should be left alone.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(2));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(2));
assertTrue(mZenModeEventLogger.getIsUserAction(2));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(2));
// Fourth event: turns on manual mode. Doesn't change effective policy so this is just a
// change in active rules. Confirm that the package UID is left unchanged.
// Because it's a manual mode change not from the system, isn't considered a user action.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(3));
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(3));
assertFalse(mZenModeEventLogger.getIsUserAction(3));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(3));
// Fourth event: changed condition on rule 2 (turning it off via condition).
// This comes from a random different UID so we expect that to remain untouched.
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED.getId(),
mZenModeEventLogger.getEventId(4));
assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(4));
assertFalse(mZenModeEventLogger.getIsUserAction(4));
assertEquals(12345, mZenModeEventLogger.getPackageUid(4));
}
@Test
public void testZenModeEventLog_channelsBypassingChanges() {
// Verify that the right thing happens when the canBypassDnd value changes.
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Turn on zen mode with a manual rule with an enabler set. This should *not* count
// as a user action, and *should* get its UID reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
CUSTOM_PKG_NAME, "", Process.SYSTEM_UID, true);
// Now change apps bypassing to true
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.areChannelsBypassingDnd = true;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
true);
// and then back to false, all without changing anything else
newConfig.areChannelsBypassingDnd = false;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), Process.SYSTEM_UID,
true);
// Turn off manual mode, call from a package: don't reset UID even though enabler is set
mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null,
CUSTOM_PKG_NAME, "", 12345, false);
// And likewise when turning it back on again
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
CUSTOM_PKG_NAME, "", 12345, false);
// These are 5 events in total.
assertEquals(5, mZenModeEventLogger.numLoggedChanges());
// First event: turns on, UID reassigned for manual mode
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(0));
assertFalse(mZenModeEventLogger.getIsUserAction(0));
assertEquals(CUSTOM_PKG_UID, mZenModeEventLogger.getPackageUid(0));
// Second event should be a policy-only change with are channels bypassing = true
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId(),
mZenModeEventLogger.getEventId(1));
assertTrue(mZenModeEventLogger.getAreChannelsBypassing(1));
// Third event also a policy-only change but with channels bypassing now false
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_POLICY_CHANGED.getId(),
mZenModeEventLogger.getEventId(2));
assertFalse(mZenModeEventLogger.getAreChannelsBypassing(2));
// Fourth event: should turn DND off, not have UID reassigned
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
mZenModeEventLogger.getEventId(3));
assertFalse(mZenModeEventLogger.getIsUserAction(3));
assertEquals(12345, mZenModeEventLogger.getPackageUid(3));
// Fifth event: turn DND back on, not have UID reassigned
assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
mZenModeEventLogger.getEventId(4));
assertFalse(mZenModeEventLogger.getIsUserAction(4));
assertEquals(12345, mZenModeEventLogger.getPackageUid(4));
}
private void setupZenConfig() {
mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;
@@ -1921,7 +2446,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig.allowSystem = false;
mZenModeHelper.mConfig.allowReminders = true;
mZenModeHelper.mConfig.allowCalls = true;
mZenModeHelper.mConfig.allowCallsFrom = PRIORITY_SENDERS_STARRED;
mZenModeHelper.mConfig.allowMessages = true;
mZenModeHelper.mConfig.allowConversations = true;
mZenModeHelper.mConfig.allowEvents = true;
mZenModeHelper.mConfig.allowRepeatCallers = true;
mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
@@ -1935,12 +2462,33 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertFalse(mZenModeHelper.mConfig.allowSystem);
assertTrue(mZenModeHelper.mConfig.allowReminders);
assertTrue(mZenModeHelper.mConfig.allowCalls);
assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConfig.allowCallsFrom);
assertTrue(mZenModeHelper.mConfig.allowMessages);
assertTrue(mZenModeHelper.mConfig.allowConversations);
assertTrue(mZenModeHelper.mConfig.allowEvents);
assertTrue(mZenModeHelper.mConfig.allowRepeatCallers);
assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelper.mConfig.suppressedVisualEffects);
}
private void checkDndProtoMatchesSetupZenConfig(DNDPolicyProto dndProto) {
assertEquals(STATE_DISALLOW, dndProto.getAlarms().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getMedia().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getSystem().getNumber());
assertEquals(STATE_ALLOW, dndProto.getReminders().getNumber());
assertEquals(STATE_ALLOW, dndProto.getCalls().getNumber());
assertEquals(PEOPLE_STARRED, dndProto.getAllowCallsFrom().getNumber());
assertEquals(STATE_ALLOW, dndProto.getMessages().getNumber());
assertEquals(STATE_ALLOW, dndProto.getEvents().getNumber());
assertEquals(STATE_ALLOW, dndProto.getRepeatCallers().getNumber());
assertEquals(STATE_ALLOW, dndProto.getFullscreen().getNumber());
assertEquals(STATE_ALLOW, dndProto.getLights().getNumber());
assertEquals(STATE_ALLOW, dndProto.getPeek().getNumber());
assertEquals(STATE_ALLOW, dndProto.getStatusBar().getNumber());
assertEquals(STATE_DISALLOW, dndProto.getBadge().getNumber());
assertEquals(STATE_ALLOW, dndProto.getAmbient().getNumber());
assertEquals(STATE_ALLOW, dndProto.getNotificationList().getNumber());
}
/**
* Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
*/