• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.notification;
18 
19 import static android.app.NotificationManager.Policy.STATE_HAS_PRIORITY_CHANNELS;
20 import static android.provider.Settings.Global.ZEN_MODE_OFF;
21 import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE;
22 import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
23 import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC;
24 import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL;
25 import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.SuppressLint;
30 import android.app.Flags;
31 import android.app.NotificationManager;
32 import android.content.pm.PackageManager;
33 import android.os.Process;
34 import android.service.notification.DNDPolicyProto;
35 import android.service.notification.ZenAdapters;
36 import android.service.notification.ZenModeConfig;
37 import android.service.notification.ZenModeConfig.ConfigOrigin;
38 import android.service.notification.ZenModeConfig.ZenRule;
39 import android.service.notification.ZenModeDiff;
40 import android.service.notification.ZenPolicy;
41 import android.util.ArrayMap;
42 import android.util.Log;
43 import android.util.Pair;
44 import android.util.Slog;
45 import android.util.proto.ProtoOutputStream;
46 
47 import com.android.internal.logging.UiEvent;
48 import com.android.internal.logging.UiEventLogger;
49 import com.android.internal.util.FrameworkStatsLog;
50 
51 import java.io.ByteArrayOutputStream;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.Objects;
56 
57 /**
58  * Class for writing DNDStateChanged atoms to the statsd log.
59  * Use ZenModeEventLoggerFake for testing.
60  */
61 class ZenModeEventLogger {
62     private static final String TAG = "ZenModeEventLogger";
63 
64     // Placeholder int for unknown zen mode, to distinguish from "off".
65     static final int ZEN_MODE_UNKNOWN = -1;
66 
67     // Special rule type for manual rule. Keep in sync with ActiveRuleType in dnd_enums.proto.
68     protected static final int ACTIVE_RULE_TYPE_MANUAL = 999;
69 
70     // Object for tracking config changes and policy changes associated with an overall zen
71     // mode change.
72     ZenModeEventLogger.ZenStateChanges mChangeState = new ZenModeEventLogger.ZenStateChanges();
73 
74     private final PackageManager mPm;
75 
ZenModeEventLogger(PackageManager pm)76     ZenModeEventLogger(PackageManager pm) {
77         mPm = pm;
78     }
79 
80     /**
81      * Enum used to log the type of DND state changed events.
82      * These use UiEvent IDs for ease of integrating with other UiEvents.
83      */
84     enum ZenStateChangedEvent implements UiEventLogger.UiEventEnum {
85         @UiEvent(doc = "DND was turned on; may additionally include policy change.")
86         DND_TURNED_ON(1368),
87         @UiEvent(doc = "DND was turned off; may additionally include policy change.")
88         DND_TURNED_OFF(1369),
89         @UiEvent(doc = "DND policy was changed but the zen mode did not change.")
90         DND_POLICY_CHANGED(1370),
91         @UiEvent(doc = "Change in DND automatic rules active, without changing mode or policy.")
92         DND_ACTIVE_RULES_CHANGED(1371);
93 
94         private final int mId;
95 
ZenStateChangedEvent(int id)96         ZenStateChangedEvent(int id) {
97             mId = id;
98         }
99 
100         @Override
getId()101         public int getId() {
102             return mId;
103         }
104     }
105 
106     /**
107      * Potentially log a zen mode change if the provided config and policy changes warrant it.
108      *
109      * @param prevInfo   ZenModeInfo (zen mode setting, config, policy) prior to this change
110      * @param newInfo    ZenModeInfo after this change takes effect
111      * @param callingUid the calling UID associated with the change; may be used to attribute the
112      *                   change to a particular package or determine if this is a user action
113      * @param origin     The origin of the Zen change.
114      */
maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid, @ConfigOrigin int origin)115     public final void maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
116             @ConfigOrigin int origin) {
117         mChangeState.init(prevInfo, newInfo, callingUid, origin);
118         if (mChangeState.shouldLogChanges()) {
119             maybeReassignCallingUid();
120             logChanges();
121         }
122 
123         // clear out the state for a fresh start next time
124         mChangeState = new ZenModeEventLogger.ZenStateChanges();
125     }
126 
127     /**
128      * Reassign callingUid in mChangeState if we have more specific information that warrants it
129      * (for instance, if the change is automatic and due to an automatic rule change).
130      *
131      * <p>When Flags.modesUi() is enabled, we reassign the calling UID to the package UID in all
132      * changes whose source is not system or system UI, as long as there is only one rule changed.
133      */
maybeReassignCallingUid()134     private void maybeReassignCallingUid() {
135         int userId = Process.INVALID_UID;
136         String packageName = null;
137 
138         // For a manual rule, we consider reassigning the UID only when the call seems to come from
139         // the system and there is a non-null enabler in the new config.
140         // We don't consider the manual rule in the old config because if a manual rule is turning
141         // off with a call from system, that could easily be a user action to explicitly turn it off
142         if (mChangeState.getChangedRuleType() == RULE_TYPE_MANUAL) {
143             if (!mChangeState.isFromSystemOrSystemUi()
144                     || mChangeState.getNewManualRuleEnabler() == null) {
145                 return;
146             }
147             packageName = mChangeState.getNewManualRuleEnabler();
148             userId = mChangeState.mNewConfig.user;  // mNewConfig must not be null if enabler exists
149         }
150 
151         // The conditions where we should consider reassigning UID for an automatic rule change
152         // (pre-modes_ui):
153         //   - we've determined it's not a user action
154         //   - our current best guess is that the calling uid is system/sysui
155         // When Flags.modesUi() is true, we get the package UID for the changed rule, as long as:
156         //   - the change does not originate from the system based on change origin
157         //   - there is only one rule changed
158         if (mChangeState.getChangedRuleType() == RULE_TYPE_AUTOMATIC) {
159             if (Flags.modesUi()) {
160                 // ignore anything whose origin is system
161                 if (mChangeState.isFromSystemOrSystemUi()) {
162                     return;
163                 }
164             } else {
165                 if (mChangeState.getIsUserAction() || !mChangeState.isFromSystemOrSystemUi()) {
166                     return;
167                 }
168             }
169 
170             // Only try to get the package UID if there's exactly one changed automatic rule. If
171             // there's more than one that changes simultaneously, this is likely to be a boot and
172             // we can leave it attributed to system.
173             ArrayMap<String, ZenModeDiff.RuleDiff> changedRules =
174                     mChangeState.getChangedAutomaticRules();
175             if (changedRules.size() != 1) {
176                 return;
177             }
178             Pair<String, Integer> ruleInfo = mChangeState.getRulePackageAndUser(
179                     changedRules.keyAt(0),
180                     changedRules.valueAt(0));
181 
182             if (ruleInfo == null || ruleInfo.first.equals(ZenModeConfig.SYSTEM_AUTHORITY)) {
183                 // leave system rules as-is
184                 return;
185             }
186 
187             packageName = ruleInfo.first;
188             userId = ruleInfo.second;
189         }
190 
191         if (userId == Process.INVALID_UID || packageName == null) {
192             // haven't found anything to look up.
193             return;
194         }
195 
196         try {
197             int uid = mPm.getPackageUidAsUser(packageName, userId);
198             mChangeState.mCallingUid = uid;
199         } catch (PackageManager.NameNotFoundException e) {
200             Slog.e(TAG, "unable to find package name " + packageName + " " + userId);
201         }
202     }
203 
204     /**
205      * Actually log all changes stored in the current change state to statsd output. This method
206      * should not be used directly by callers; visible for override by subclasses.
207      */
logChanges()208     void logChanges() {
209         FrameworkStatsLog.write(FrameworkStatsLog.DND_STATE_CHANGED,
210                 /* int32 event_id = 1 */ mChangeState.getEventId().getId(),
211                 /* android.stats.dnd.ZenMode new_mode = 2 */ mChangeState.mNewZenMode,
212                 /* android.stats.dnd.ZenMode previous_mode = 3 */ mChangeState.mPrevZenMode,
213                 /* android.stats.dnd.RuleType rule_type = 4 */ mChangeState.getChangedRuleType(),
214                 /* int32 num_rules_active = 5 */ mChangeState.getNumRulesActive(),
215                 /* bool user_action = 6 */ mChangeState.getIsUserAction(),
216                 /* int32 package_uid = 7 */ mChangeState.getPackageUid(),
217                 /* DNDPolicyProto current_policy = 8 */ mChangeState.getDNDPolicyProto(),
218                 /* bool are_channels_bypassing = 9 */ mChangeState.getAreChannelsBypassing(),
219                 /* ActiveRuleType active_rule_types = 10 */ mChangeState.getActiveRuleTypes(),
220                 /* ChangeOrigin change_origin = 11 */ mChangeState.getChangeOrigin());
221     }
222 
223     /**
224      * Helper class for storing the set of information about a zen mode configuration at a specific
225      * time: the current zen mode setting, ZenModeConfig, and consolidated policy (a result of
226      * evaluating all active zen rules at the time).
227      */
228     public static class ZenModeInfo {
229         final int mZenMode;
230         final ZenModeConfig mConfig;
231         final NotificationManager.Policy mPolicy;
232 
ZenModeInfo(int zenMode, ZenModeConfig config, NotificationManager.Policy policy)233         ZenModeInfo(int zenMode, ZenModeConfig config, NotificationManager.Policy policy) {
234             mZenMode = zenMode;
235             // Store a copy of configs & policies to not accidentally pick up any further changes
236             mConfig = config != null ? config.copy() : null;
237             mPolicy = policy != null ? policy.copy() : null;
238         }
239     }
240 
241     /**
242      * Class used to track overall changes in zen mode, since changes such as config updates happen
243      * in multiple stages (first changing the config, then re-evaluating zen mode and the
244      * consolidated policy), and which contains the logic of 1) whether to log the zen mode change
245      * and 2) deriving the properties to log.
246      */
247     static class ZenStateChanges {
248         int mPrevZenMode = ZEN_MODE_UNKNOWN;
249         int mNewZenMode = ZEN_MODE_UNKNOWN;
250         ZenModeConfig mPrevConfig, mNewConfig;
251         NotificationManager.Policy mPrevPolicy, mNewPolicy;
252         int mCallingUid = Process.INVALID_UID;
253         @ConfigOrigin
254         int mOrigin = ZenModeConfig.ORIGIN_UNKNOWN;
255 
init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid, @ConfigOrigin int origin)256         private void init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
257                 @ConfigOrigin int origin) {
258             // previous & new may be the same -- that would indicate that zen mode hasn't changed.
259             mPrevZenMode = prevInfo.mZenMode;
260             mNewZenMode = newInfo.mZenMode;
261             mPrevConfig = prevInfo.mConfig;
262             mNewConfig = newInfo.mConfig;
263             mPrevPolicy = prevInfo.mPolicy;
264             mNewPolicy = newInfo.mPolicy;
265             mCallingUid = callingUid;
266             mOrigin = origin;
267         }
268 
269         /**
270          * Returns whether there is a policy diff represented by this change. This doesn't count
271          * if the previous policy is null, as that would indicate having no information rather than
272          * having no previous policy.
273          */
hasPolicyDiff()274         private boolean hasPolicyDiff() {
275             return mPrevPolicy != null && !Objects.equals(mPrevPolicy, mNewPolicy);
276         }
277 
278         /**
279          * Whether the set of changes encapsulated in this state should be logged. This should only
280          * be called after methods to store config and zen mode info.
281          */
shouldLogChanges()282         private boolean shouldLogChanges() {
283             // Did zen mode change from off to on or vice versa? If so, log in all cases.
284             if (zenModeFlipped()) {
285                 return true;
286             }
287 
288             if (hasActiveRuleCountDiff()) {
289                 // Rules with INTERRUPTION_FILTER_ALL can apply effects, so we want to log when they
290                 // become active/inactive, even though DND itself (as in "notification blocking")
291                 // is off.
292                 return true;
293             }
294 
295             // If zen mode didn't change, did the policy or number of active rules change? We only
296             // care about changes that take effect while zen mode is on, so make sure the current
297             // zen mode is not "OFF"
298             if (mNewZenMode == ZEN_MODE_OFF) {
299                 return false;
300             }
301             return hasPolicyDiff() || hasActiveRuleCountDiff();
302         }
303 
304         // Does the difference in zen mode go from off to on or vice versa?
zenModeFlipped()305         private boolean zenModeFlipped() {
306             if (mPrevZenMode == mNewZenMode) {
307                 return false;
308             }
309 
310             // then it flipped if one or the other is off. (there's only one off state; there are
311             // multiple states one could consider "on")
312             return mPrevZenMode == ZEN_MODE_OFF || mNewZenMode == ZEN_MODE_OFF;
313         }
314 
315         // Helper methods below to fill out the atom contents below:
316 
317         /**
318          * Based on the changes, returns the event ID corresponding to the change. Assumes that
319          * shouldLogChanges() is true and already checked (and will Log.wtf if not true).
320          */
getEventId()321         ZenStateChangedEvent getEventId() {
322             if (!shouldLogChanges()) {
323                 Log.wtf(TAG, "attempt to get DNDStateChanged fields without shouldLog=true");
324             }
325             if (zenModeFlipped()) {
326                 if (mPrevZenMode == ZEN_MODE_OFF) {
327                     return ZenStateChangedEvent.DND_TURNED_ON;
328                 } else {
329                     return ZenStateChangedEvent.DND_TURNED_OFF;
330                 }
331             }
332 
333             if (mNewZenMode == ZEN_MODE_OFF) {
334                 // If the mode is OFF -> OFF then there cannot be any *effective* change to policy.
335                 // (Note that, in theory, a policy diff is impossible since we don't merge the
336                 // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check).
337                 if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
338                     Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled");
339                 }
340                 return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
341             }
342 
343             // zen mode didn't change; we must be here because of a policy change or rule change
344             if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
345                 return ZenStateChangedEvent.DND_POLICY_CHANGED;
346             }
347 
348             // Also no policy change, so it has to be a rule change
349             return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
350         }
351 
352         /**
353          * Based on the config diff, determine which type of rule changed (or "unknown" to indicate
354          * unknown or neither).
355          * In the (probably somewhat unusual) case that there are both, manual takes precedence over
356          * automatic.
357          */
getChangedRuleType()358         int getChangedRuleType() {
359             ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
360             if (!diff.hasDiff()) {
361                 // no diff in the config. this probably shouldn't happen, but we can consider it
362                 // unknown (given that if zen mode changes it is usually accompanied by some rule
363                 // turning on or off, which should cause a config diff).
364                 return RULE_TYPE_UNKNOWN;
365             }
366 
367             ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff();
368             if (manualDiff != null && manualDiff.hasDiff()) {
369                 // a diff in the manual rule doesn't *necessarily* mean that it's responsible for
370                 // the change -- only if it's been added or removed or updated.
371                 if (manualDiff.wasAdded() || manualDiff.wasRemoved()
372                         || (Flags.modesUi()
373                         && (manualDiff.becameActive() || manualDiff.becameInactive()))) {
374                     return RULE_TYPE_MANUAL;
375                 }
376             }
377 
378             ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
379             if (autoDiffs != null) {
380                 for (ZenModeDiff.RuleDiff d : autoDiffs.values()) {
381                     if (d != null && d.hasDiff()) {
382                         // If the rule became active or inactive, then this is probably relevant.
383                         if (d.becameActive() || d.becameInactive()) {
384                             return RULE_TYPE_AUTOMATIC;
385                         }
386                     }
387                 }
388             }
389             return RULE_TYPE_UNKNOWN;
390         }
391 
392         /**
393          * Returns whether the previous config and new config have a different number of active
394          * automatic or manual rules.
395          */
hasActiveRuleCountDiff()396         private boolean hasActiveRuleCountDiff() {
397             return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig);
398         }
399 
400         /**
401          * Get a list of the active rules in the provided config. This is a helper function for
402          * other methods that then use this information to get the number and type of active
403          * rules available.
404          */
405         @SuppressLint("WrongConstant")  // special case for log-only type on manual rule
406         @NonNull
activeRulesList(ZenModeConfig config)407         List<ZenRule> activeRulesList(ZenModeConfig config) {
408             ArrayList<ZenRule> rules = new ArrayList<>();
409             if (config == null) {
410                 return rules;
411             }
412             if (config.isManualActive()) {
413                 // We make a copy and set the rule type so that the correct value gets logged.
414                 ZenRule rule = config.manualRule.copy();
415                 rule.type = ACTIVE_RULE_TYPE_MANUAL;
416                 rules.add(rule);
417             }
418 
419             if (config.automaticRules != null) {
420                 for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
421                     if (rule != null && rule.isActive()) {
422                         rules.add(rule);
423                     }
424                 }
425             }
426             return rules;
427         }
428 
429         /**
430          * Get the number of active rules represented in a zen mode config. Because this is based
431          * on a config, this does not take into account the zen mode at the time of the config,
432          * which means callers need to take the zen mode into account for whether the rules are
433          * actually active.
434          */
numActiveRulesInConfig(ZenModeConfig config)435         int numActiveRulesInConfig(ZenModeConfig config) {
436             return activeRulesList(config).size();
437         }
438 
439         // Determine the number of (automatic & manual) rules active after the change takes place.
getNumRulesActive()440         int getNumRulesActive() {
441             return numActiveRulesInConfig(mNewConfig);
442         }
443 
444         /**
445          * Return a list of the types of each of the active rules in the configuration (sorted by
446          * the numerical value of the type, and including duplicates).
447          */
getActiveRuleTypes()448         int[] getActiveRuleTypes() {
449             ArrayList<Integer> activeTypes = new ArrayList<>();
450             List<ZenRule> activeRules = activeRulesList(mNewConfig);
451             if (activeRules.size() == 0) {
452                 return new int[0];
453             }
454 
455             for (ZenRule rule : activeRules) {
456                 activeTypes.add(rule.type);
457             }
458 
459             // Sort the list of active types to have a consistent order in the atom
460             Collections.sort(activeTypes);
461             int[] out = new int[activeTypes.size()];
462             for (int i = 0; i < activeTypes.size(); i++) {
463                 out[i] = activeTypes.get(i);
464             }
465             return out;
466         }
467 
468         /** Return whether the changes observed are due to a user action. */
getIsUserAction()469         boolean getIsUserAction() {
470             return mOrigin == ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI
471                     || mOrigin == ZenModeConfig.ORIGIN_USER_IN_APP;
472         }
473 
isFromSystemOrSystemUi()474         boolean isFromSystemOrSystemUi() {
475             return mOrigin == ZenModeConfig.ORIGIN_INIT
476                     || mOrigin == ZenModeConfig.ORIGIN_INIT_USER
477                     || mOrigin == ZenModeConfig.ORIGIN_SYSTEM
478                     || mOrigin == ZenModeConfig.ORIGIN_RESTORE_BACKUP;
479         }
480 
481         /**
482          * Get the package UID associated with this change, which is just the calling UID for the
483          * relevant method changes. This may get reset by ZenModeEventLogger, which has access to
484          * a PackageManager to get an appropriate UID for a package.
485          */
getPackageUid()486         int getPackageUid() {
487             return mCallingUid;
488         }
489 
490         /**
491          * Get the config change origin associated with this change, which is stored in mOrigin.
492          * Only useable if modes_ui is true.
493          */
getChangeOrigin()494         int getChangeOrigin() {
495             if (Flags.modesUi()) {
496                 return mOrigin;
497             }
498             return 0;
499         }
500 
501         /**
502          * Convert the new policy to a DNDPolicyProto format for output in logs.
503          *
504          * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules
505          * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns
506          * {@code null} (which will be mapped to a missing submessage in the proto). Although this
507          * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it
508          * makes sense for logging since that policy is not actually influencing anything.
509          */
510         @Nullable
getDNDPolicyProto()511         byte[] getDNDPolicyProto() {
512             if (mNewZenMode == ZEN_MODE_OFF) {
513                 return null;
514             }
515 
516             ByteArrayOutputStream bytes = new ByteArrayOutputStream();
517             ProtoOutputStream proto = new ProtoOutputStream(bytes);
518 
519             // While we don't expect this to be null at any point, guard against any weird cases.
520             if (mNewPolicy != null) {
521                 proto.write(DNDPolicyProto.CALLS, toState(mNewPolicy.allowCalls()));
522                 proto.write(DNDPolicyProto.REPEAT_CALLERS,
523                         toState(mNewPolicy.allowRepeatCallers()));
524                 proto.write(DNDPolicyProto.MESSAGES, toState(mNewPolicy.allowMessages()));
525                 proto.write(DNDPolicyProto.CONVERSATIONS, toState(mNewPolicy.allowConversations()));
526                 proto.write(DNDPolicyProto.REMINDERS, toState(mNewPolicy.allowReminders()));
527                 proto.write(DNDPolicyProto.EVENTS, toState(mNewPolicy.allowEvents()));
528                 proto.write(DNDPolicyProto.ALARMS, toState(mNewPolicy.allowAlarms()));
529                 proto.write(DNDPolicyProto.MEDIA, toState(mNewPolicy.allowMedia()));
530                 proto.write(DNDPolicyProto.SYSTEM, toState(mNewPolicy.allowSystem()));
531 
532                 proto.write(DNDPolicyProto.FULLSCREEN, toState(mNewPolicy.showFullScreenIntents()));
533                 proto.write(DNDPolicyProto.LIGHTS, toState(mNewPolicy.showLights()));
534                 proto.write(DNDPolicyProto.PEEK, toState(mNewPolicy.showPeeking()));
535                 proto.write(DNDPolicyProto.STATUS_BAR, toState(mNewPolicy.showStatusBarIcons()));
536                 proto.write(DNDPolicyProto.BADGE, toState(mNewPolicy.showBadges()));
537                 proto.write(DNDPolicyProto.AMBIENT, toState(mNewPolicy.showAmbient()));
538                 proto.write(DNDPolicyProto.NOTIFICATION_LIST,
539                         toState(mNewPolicy.showInNotificationList()));
540 
541                 // Note: The DND policy proto uses the people type enum from *ZenPolicy* and not
542                 // *NotificationManager.Policy* (which is the type of the consolidated policy).
543                 // This applies to both call and message senders, but not conversation senders,
544                 // where they use the same enum values.
545                 proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
546                         ZenAdapters.prioritySendersToPeopleType(
547                                 mNewPolicy.allowCallsFrom()));
548                 proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
549                         ZenAdapters.prioritySendersToPeopleType(
550                                 mNewPolicy.allowMessagesFrom()));
551                 proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
552                         mNewPolicy.allowConversationsFrom());
553                 proto.write(DNDPolicyProto.ALLOW_CHANNELS,
554                         mNewPolicy.allowPriorityChannels()
555                                 ? CHANNEL_POLICY_PRIORITY
556                                 : CHANNEL_POLICY_NONE);
557             } else {
558                 Log.wtf(TAG, "attempted to write zen mode log event with null policy");
559             }
560 
561             proto.flush();
562             return bytes.toByteArray();
563         }
564 
565         /**
566          * Get whether any channels are bypassing DND based on the current new policy.
567          */
getAreChannelsBypassing()568         boolean getAreChannelsBypassing() {
569             if (mNewPolicy != null) {
570                 return (mNewPolicy.state & STATE_HAS_PRIORITY_CHANNELS) != 0;
571             }
572             return false;
573         }
574 
hasChannelsBypassingDiff()575         private boolean hasChannelsBypassingDiff() {
576             boolean prevChannelsBypassing = mPrevPolicy != null
577                     ? (mPrevPolicy.state & STATE_HAS_PRIORITY_CHANNELS) != 0 : false;
578             return prevChannelsBypassing != getAreChannelsBypassing();
579         }
580 
581         /**
582          * helper method to turn a boolean allow or disallow state into STATE_ALLOW or
583          * STATE_DISALLOW (there is no concept of "unset" in NM.Policy.)
584          */
toState(boolean allow)585         private int toState(boolean allow) {
586             return allow ? ZenPolicy.STATE_ALLOW : ZenPolicy.STATE_DISALLOW;
587         }
588 
589         /**
590          * Get the list of automatic rules that have any diff (as a List of ZenModeDiff.RuleDiff).
591          * Returns an empty list if there isn't anything.
592          */
getChangedAutomaticRules()593         private @NonNull ArrayMap<String, ZenModeDiff.RuleDiff> getChangedAutomaticRules() {
594             ArrayMap<String, ZenModeDiff.RuleDiff> ruleDiffs = new ArrayMap<>();
595 
596             ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
597             if (!diff.hasDiff()) {
598                 return ruleDiffs;
599             }
600 
601             ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
602             if (autoDiffs != null) {
603                 return autoDiffs;
604             }
605             return ruleDiffs;
606         }
607 
608         /**
609          * Get the package name associated with this rule's owner, given its id and associated
610          * RuleDiff, as well as the user ID associated with the config it was found in. Returns null
611          * if none could be found.
612          */
getRulePackageAndUser(String id, ZenModeDiff.RuleDiff diff)613         private Pair<String, Integer> getRulePackageAndUser(String id, ZenModeDiff.RuleDiff diff) {
614             // look for the rule info in the new config unless the rule was deleted.
615             ZenModeConfig configForSearch = mNewConfig;
616             if (diff.wasRemoved()) {
617                 configForSearch = mPrevConfig;
618             }
619 
620             if (configForSearch == null) {
621                 return null;
622             }
623 
624             ZenModeConfig.ZenRule rule = configForSearch.automaticRules.getOrDefault(id, null);
625             if (rule != null) {
626                 if (rule.component != null) {
627                     return new Pair(rule.component.getPackageName(), configForSearch.user);
628                 }
629                 if (rule.configurationActivity != null) {
630                     return new Pair(rule.configurationActivity.getPackageName(),
631                             configForSearch.user);
632                 }
633             }
634             return null;
635         }
636 
637         /**
638          * Get the package name listed as the manual rule "enabler", if it exists in the new config.
639          */
getNewManualRuleEnabler()640         private String getNewManualRuleEnabler() {
641             if (mNewConfig == null || mNewConfig.manualRule == null) {
642                 return null;
643             }
644             return mNewConfig.manualRule.enabler;
645         }
646 
647         /**
648          * Makes a copy for storing intermediate state for testing purposes.
649          */
copy()650         protected ZenStateChanges copy() {
651             ZenStateChanges copy = new ZenStateChanges();
652             copy.mPrevZenMode = mPrevZenMode;
653             copy.mNewZenMode = mNewZenMode;
654             copy.mPrevConfig = mPrevConfig.copy();
655             copy.mNewConfig = mNewConfig.copy();
656             copy.mPrevPolicy = mPrevPolicy.copy();
657             copy.mNewPolicy = mNewPolicy.copy();
658             copy.mCallingUid = mCallingUid;
659             copy.mOrigin = mOrigin;
660             return copy;
661         }
662     }
663 }
664