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_CHANNELS_BYPASSING_DND;
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.ConfigChangeOrigin;
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, @ConfigChangeOrigin int origin)115     public final void maybeLogZenChange(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
116             @ConfigChangeOrigin 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      */
maybeReassignCallingUid()131     private void maybeReassignCallingUid() {
132         int userId = Process.INVALID_UID;
133         String packageName = null;
134 
135         // For a manual rule, we consider reassigning the UID only when the call seems to come from
136         // the system and there is a non-null enabler in the new config.
137         // We don't consider the manual rule in the old config because if a manual rule is turning
138         // off with a call from system, that could easily be a user action to explicitly turn it off
139         if (mChangeState.getChangedRuleType() == RULE_TYPE_MANUAL) {
140             if (!mChangeState.isFromSystemOrSystemUi()
141                     || mChangeState.getNewManualRuleEnabler() == null) {
142                 return;
143             }
144             packageName = mChangeState.getNewManualRuleEnabler();
145             userId = mChangeState.mNewConfig.user;  // mNewConfig must not be null if enabler exists
146         }
147 
148         // The conditions where we should consider reassigning UID for an automatic rule change:
149         //   - we've determined it's not a user action
150         //   - our current best guess is that the calling uid is system/sysui
151         if (mChangeState.getChangedRuleType() == RULE_TYPE_AUTOMATIC) {
152             if (mChangeState.getIsUserAction() || !mChangeState.isFromSystemOrSystemUi()) {
153                 return;
154             }
155 
156             // Only try to get the package UID if there's exactly one changed automatic rule. If
157             // there's more than one that changes simultaneously, this is likely to be a boot and
158             // we can leave it attributed to system.
159             ArrayMap<String, ZenModeDiff.RuleDiff> changedRules =
160                     mChangeState.getChangedAutomaticRules();
161             if (changedRules.size() != 1) {
162                 return;
163             }
164             Pair<String, Integer> ruleInfo = mChangeState.getRulePackageAndUser(
165                     changedRules.keyAt(0),
166                     changedRules.valueAt(0));
167 
168             if (ruleInfo == null || ruleInfo.first.equals(ZenModeConfig.SYSTEM_AUTHORITY)) {
169                 // leave system rules as-is
170                 return;
171             }
172 
173             packageName = ruleInfo.first;
174             userId = ruleInfo.second;
175         }
176 
177         if (userId == Process.INVALID_UID || packageName == null) {
178             // haven't found anything to look up.
179             return;
180         }
181 
182         try {
183             int uid = mPm.getPackageUidAsUser(packageName, userId);
184             mChangeState.mCallingUid = uid;
185         } catch (PackageManager.NameNotFoundException e) {
186             Slog.e(TAG, "unable to find package name " + packageName + " " + userId);
187         }
188     }
189 
190     /**
191      * Actually log all changes stored in the current change state to statsd output. This method
192      * should not be used directly by callers; visible for override by subclasses.
193      */
logChanges()194     void logChanges() {
195         FrameworkStatsLog.write(FrameworkStatsLog.DND_STATE_CHANGED,
196                 /* int32 event_id = 1 */ mChangeState.getEventId().getId(),
197                 /* android.stats.dnd.ZenMode new_mode = 2 */ mChangeState.mNewZenMode,
198                 /* android.stats.dnd.ZenMode previous_mode = 3 */ mChangeState.mPrevZenMode,
199                 /* android.stats.dnd.RuleType rule_type = 4 */ mChangeState.getChangedRuleType(),
200                 /* int32 num_rules_active = 5 */ mChangeState.getNumRulesActive(),
201                 /* bool user_action = 6 */ mChangeState.getIsUserAction(),
202                 /* int32 package_uid = 7 */ mChangeState.getPackageUid(),
203                 /* DNDPolicyProto current_policy = 8 */ mChangeState.getDNDPolicyProto(),
204                 /* bool are_channels_bypassing = 9 */ mChangeState.getAreChannelsBypassing(),
205                 /* ActiveRuleType active_rule_types = 10 */ mChangeState.getActiveRuleTypes());
206     }
207 
208     /**
209      * Helper class for storing the set of information about a zen mode configuration at a specific
210      * time: the current zen mode setting, ZenModeConfig, and consolidated policy (a result of
211      * evaluating all active zen rules at the time).
212      */
213     public static class ZenModeInfo {
214         final int mZenMode;
215         final ZenModeConfig mConfig;
216         final NotificationManager.Policy mPolicy;
217 
ZenModeInfo(int zenMode, ZenModeConfig config, NotificationManager.Policy policy)218         ZenModeInfo(int zenMode, ZenModeConfig config, NotificationManager.Policy policy) {
219             mZenMode = zenMode;
220             // Store a copy of configs & policies to not accidentally pick up any further changes
221             mConfig = config != null ? config.copy() : null;
222             mPolicy = policy != null ? policy.copy() : null;
223         }
224     }
225 
226     /**
227      * Class used to track overall changes in zen mode, since changes such as config updates happen
228      * in multiple stages (first changing the config, then re-evaluating zen mode and the
229      * consolidated policy), and which contains the logic of 1) whether to log the zen mode change
230      * and 2) deriving the properties to log.
231      */
232     static class ZenStateChanges {
233         int mPrevZenMode = ZEN_MODE_UNKNOWN;
234         int mNewZenMode = ZEN_MODE_UNKNOWN;
235         ZenModeConfig mPrevConfig, mNewConfig;
236         NotificationManager.Policy mPrevPolicy, mNewPolicy;
237         int mCallingUid = Process.INVALID_UID;
238         @ConfigChangeOrigin int mOrigin = ZenModeConfig.UPDATE_ORIGIN_UNKNOWN;
239 
init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid, @ConfigChangeOrigin int origin)240         private void init(ZenModeInfo prevInfo, ZenModeInfo newInfo, int callingUid,
241                 @ConfigChangeOrigin int origin) {
242             // previous & new may be the same -- that would indicate that zen mode hasn't changed.
243             mPrevZenMode = prevInfo.mZenMode;
244             mNewZenMode = newInfo.mZenMode;
245             mPrevConfig = prevInfo.mConfig;
246             mNewConfig = newInfo.mConfig;
247             mPrevPolicy = prevInfo.mPolicy;
248             mNewPolicy = newInfo.mPolicy;
249             mCallingUid = callingUid;
250             mOrigin = origin;
251         }
252 
253         /**
254          * Returns whether there is a policy diff represented by this change. This doesn't count
255          * if the previous policy is null, as that would indicate having no information rather than
256          * having no previous policy.
257          */
hasPolicyDiff()258         private boolean hasPolicyDiff() {
259             return mPrevPolicy != null && !Objects.equals(mPrevPolicy, mNewPolicy);
260         }
261 
262         /**
263          * Whether the set of changes encapsulated in this state should be logged. This should only
264          * be called after methods to store config and zen mode info.
265          */
shouldLogChanges()266         private boolean shouldLogChanges() {
267             // Did zen mode change from off to on or vice versa? If so, log in all cases.
268             if (zenModeFlipped()) {
269                 return true;
270             }
271 
272             if (Flags.modesApi() && hasActiveRuleCountDiff()) {
273                 // Rules with INTERRUPTION_FILTER_ALL were always possible but before MODES_API
274                 // they were completely useless; now they can apply effects, so we want to log
275                 // when they become active/inactive, even though DND itself (as in "notification
276                 // blocking") is off.
277                 return true;
278             }
279 
280             // If zen mode didn't change, did the policy or number of active rules change? We only
281             // care about changes that take effect while zen mode is on, so make sure the current
282             // zen mode is not "OFF"
283             if (mNewZenMode == ZEN_MODE_OFF) {
284                 return false;
285             }
286             return hasPolicyDiff() || hasActiveRuleCountDiff();
287         }
288 
289         // Does the difference in zen mode go from off to on or vice versa?
zenModeFlipped()290         private boolean zenModeFlipped() {
291             if (mPrevZenMode == mNewZenMode) {
292                 return false;
293             }
294 
295             // then it flipped if one or the other is off. (there's only one off state; there are
296             // multiple states one could consider "on")
297             return mPrevZenMode == ZEN_MODE_OFF || mNewZenMode == ZEN_MODE_OFF;
298         }
299 
300         // Helper methods below to fill out the atom contents below:
301 
302         /**
303          * Based on the changes, returns the event ID corresponding to the change. Assumes that
304          * shouldLogChanges() is true and already checked (and will Log.wtf if not true).
305          */
getEventId()306         ZenStateChangedEvent getEventId() {
307             if (!shouldLogChanges()) {
308                 Log.wtf(TAG, "attempt to get DNDStateChanged fields without shouldLog=true");
309             }
310             if (zenModeFlipped()) {
311                 if (mPrevZenMode == ZEN_MODE_OFF) {
312                     return ZenStateChangedEvent.DND_TURNED_ON;
313                 } else {
314                     return ZenStateChangedEvent.DND_TURNED_OFF;
315                 }
316             }
317 
318             if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
319                 // If the mode is OFF -> OFF then there cannot be any *effective* change to policy.
320                 // (Note that, in theory, a policy diff is impossible since we don't merge the
321                 // policies of INTERRUPTION_FILTER_ALL rules; this is a "just in case" check).
322                 if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
323                     Log.wtf(TAG, "Detected policy diff even though DND is OFF and not toggled");
324                 }
325                 return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
326             }
327 
328             // zen mode didn't change; we must be here because of a policy change or rule change
329             if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
330                 return ZenStateChangedEvent.DND_POLICY_CHANGED;
331             }
332 
333             // Also no policy change, so it has to be a rule change
334             return ZenStateChangedEvent.DND_ACTIVE_RULES_CHANGED;
335         }
336 
337         /**
338          * Based on the config diff, determine which type of rule changed (or "unknown" to indicate
339          * unknown or neither).
340          * In the (probably somewhat unusual) case that there are both, manual takes precedence over
341          * automatic.
342          */
getChangedRuleType()343         int getChangedRuleType() {
344             ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
345             if (!diff.hasDiff()) {
346                 // no diff in the config. this probably shouldn't happen, but we can consider it
347                 // unknown (given that if zen mode changes it is usually accompanied by some rule
348                 // turning on or off, which should cause a config diff).
349                 return RULE_TYPE_UNKNOWN;
350             }
351 
352             ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff();
353             if (manualDiff != null && manualDiff.hasDiff()) {
354                 // a diff in the manual rule doesn't *necessarily* mean that it's responsible for
355                 // the change -- only if it's been added or removed or updated.
356                 if (manualDiff.wasAdded() || manualDiff.wasRemoved()
357                         || (Flags.modesUi()
358                         && (manualDiff.becameActive() || manualDiff.becameInactive()))) {
359                     return RULE_TYPE_MANUAL;
360                 }
361             }
362 
363             ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
364             if (autoDiffs != null) {
365                 for (ZenModeDiff.RuleDiff d : autoDiffs.values()) {
366                     if (d != null && d.hasDiff()) {
367                         // If the rule became active or inactive, then this is probably relevant.
368                         if (d.becameActive() || d.becameInactive()) {
369                             return RULE_TYPE_AUTOMATIC;
370                         }
371                     }
372                 }
373             }
374             return RULE_TYPE_UNKNOWN;
375         }
376 
377         /**
378          * Returns whether the previous config and new config have a different number of active
379          * automatic or manual rules.
380          */
hasActiveRuleCountDiff()381         private boolean hasActiveRuleCountDiff() {
382             return numActiveRulesInConfig(mPrevConfig) != numActiveRulesInConfig(mNewConfig);
383         }
384 
385         /**
386          * Get a list of the active rules in the provided config. This is a helper function for
387          * other methods that then use this information to get the number and type of active
388          * rules available.
389          */
390         @SuppressLint("WrongConstant")  // special case for log-only type on manual rule
activeRulesList(ZenModeConfig config)391         @NonNull List<ZenRule> activeRulesList(ZenModeConfig config) {
392             ArrayList<ZenRule> rules = new ArrayList<>();
393             if (config == null) {
394                 return rules;
395             }
396             if (config.isManualActive()) {
397                 // We make a copy and set the rule type so that the correct value gets logged.
398                 ZenRule rule = config.manualRule.copy();
399                 rule.type = ACTIVE_RULE_TYPE_MANUAL;
400                 rules.add(rule);
401             }
402 
403             if (config.automaticRules != null) {
404                 for (ZenModeConfig.ZenRule rule : config.automaticRules.values()) {
405                     if (rule != null && rule.isAutomaticActive()) {
406                         rules.add(rule);
407                     }
408                 }
409             }
410             return rules;
411         }
412 
413         /**
414          * Get the number of active rules represented in a zen mode config. Because this is based
415          * on a config, this does not take into account the zen mode at the time of the config,
416          * which means callers need to take the zen mode into account for whether the rules are
417          * actually active.
418          */
numActiveRulesInConfig(ZenModeConfig config)419         int numActiveRulesInConfig(ZenModeConfig config) {
420             return activeRulesList(config).size();
421         }
422 
423         // Determine the number of (automatic & manual) rules active after the change takes place.
getNumRulesActive()424         int getNumRulesActive() {
425             if (!Flags.modesApi()) {
426                 // If the zen mode has turned off, that means nothing can be active.
427                 if (mNewZenMode == ZEN_MODE_OFF) {
428                     return 0;
429                 }
430             }
431             return numActiveRulesInConfig(mNewConfig);
432         }
433 
434         /**
435          * Return a list of the types of each of the active rules in the configuration.
436          * Only available when {@code MODES_API} is active; otherwise returns an empty list.
437          */
getActiveRuleTypes()438         int[] getActiveRuleTypes() {
439             if (!Flags.modesApi()) {
440                 return new int[0];
441             }
442 
443             ArrayList<Integer> activeTypes = new ArrayList<>();
444             List<ZenRule> activeRules = activeRulesList(mNewConfig);
445             if (activeRules.size() == 0) {
446                 return new int[0];
447             }
448 
449             for (ZenRule rule : activeRules) {
450                 activeTypes.add(rule.type);
451             }
452 
453             // Sort the list of active types to have a consistent order in the atom
454             Collections.sort(activeTypes);
455             int[] out = new int[activeTypes.size()];
456             for (int i = 0; i < activeTypes.size(); i++) {
457                 out[i] = activeTypes.get(i);
458             }
459             return out;
460         }
461 
462         /**
463          * Return our best guess as to whether the changes observed are due to a user action.
464          * Note that this (before {@code MODES_API}) won't be 100% accurate as we can't necessarily
465          * distinguish between a system uid call indicating "user interacted with Settings" vs "a
466          * system app changed something automatically".
467          */
getIsUserAction()468         boolean getIsUserAction() {
469             if (Flags.modesApi()) {
470                 return mOrigin == ZenModeConfig.UPDATE_ORIGIN_USER;
471             }
472 
473             // Approach for pre-MODES_API:
474             //   - if manual rule turned on or off, the calling UID is system, and the new manual
475             //     rule does not have an enabler set, guess that this is likely to be a user action.
476             //     This may represent a system app turning on DND automatically, but we guess "user"
477             //     in this case.
478             //         - note that this has a known failure mode of "manual rule turning off
479             //           automatically after the default time runs out". We currently have no way
480             //           of distinguishing this case from a user manually turning off the rule.
481             //         - the reason for checking the enabler field is that a call may look like it's
482             //           coming from a system UID, but if an enabler is set then the request came
483             //           from an external source. "enabler" will be blank when manual rule is turned
484             //           on from Quick Settings or Settings.
485             //   - if an automatic rule's state changes in whether it is "enabled", then
486             //     that is probably a user action.
487             //   - if an automatic rule goes from "not snoozing" to "snoozing", that is probably
488             //     a user action; that means that the user temporarily turned off DND associated
489             //     with that rule.
490             //   - if an automatic rule becomes active but does *not* change in its enabled state
491             //     (covered by a previous case anyway), we guess that this is an automatic change.
492             //   - if a rule is added or removed and the call comes from the system, we guess that
493             //     this is a user action (as system rules can't be added or removed without a user
494             //     action).
495             switch (getChangedRuleType()) {
496                 case RULE_TYPE_MANUAL:
497                     // TODO(b/278888961): Distinguish the automatically-turned-off state
498                     return isFromSystemOrSystemUi() && (getNewManualRuleEnabler() == null);
499                 case RULE_TYPE_AUTOMATIC:
500                     for (ZenModeDiff.RuleDiff d : getChangedAutomaticRules().values()) {
501                         if (d.wasAdded() || d.wasRemoved()) {
502                             // If the change comes from system, a rule being added/removed indicates
503                             // a likely user action. From an app, it's harder to know for sure.
504                             return isFromSystemOrSystemUi();
505                         }
506                         ZenModeDiff.FieldDiff enabled = d.getDiffForField(
507                                 ZenModeDiff.RuleDiff.FIELD_ENABLED);
508                         if (enabled != null && enabled.hasDiff()) {
509                             return true;
510                         }
511                         ZenModeDiff.FieldDiff snoozing = d.getDiffForField(
512                                 ZenModeDiff.RuleDiff.FIELD_SNOOZING);
513                         if (snoozing != null && snoozing.hasDiff() && (boolean) snoozing.to()) {
514                             return true;
515                         }
516                     }
517                     // If the change was in an automatic rule and none of the "probably triggered
518                     // by a user" cases apply, then it's probably an automatic change.
519                     return false;
520                 case RULE_TYPE_UNKNOWN:
521                 default:
522             }
523 
524             // If the change wasn't in a rule, but was in the zen policy: consider to be user action
525             // if the calling uid is system
526             if (hasPolicyDiff() || hasChannelsBypassingDiff()) {
527                 return mCallingUid == Process.SYSTEM_UID;
528             }
529 
530             // don't know, or none of the other things triggered; assume not a user action
531             return false;
532         }
533 
isFromSystemOrSystemUi()534         boolean isFromSystemOrSystemUi() {
535             return mOrigin == ZenModeConfig.UPDATE_ORIGIN_INIT
536                     || mOrigin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
537                     || mOrigin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
538                     || mOrigin == ZenModeConfig.UPDATE_ORIGIN_RESTORE_BACKUP;
539         }
540 
541         /**
542          * Get the package UID associated with this change, which is just the calling UID for the
543          * relevant method changes. This may get reset by ZenModeEventLogger, which has access to
544          * a PackageManager to get an appropriate UID for a package.
545          */
getPackageUid()546         int getPackageUid() {
547             return mCallingUid;
548         }
549 
550         /**
551          * Convert the new policy to a DNDPolicyProto format for output in logs.
552          *
553          * <p>If {@code mNewZenMode} is {@code ZEN_MODE_OFF} (which can mean either no rules
554          * active, or only rules with {@code INTERRUPTION_FILTER_ALL} active) then this returns
555          * {@code null} (which will be mapped to a missing submessage in the proto). Although this
556          * is not the value of {@code NotificationManager#getConsolidatedNotificationPolicy()}, it
557          * makes sense for logging since that policy is not actually influencing anything.
558          */
559         @Nullable
getDNDPolicyProto()560         byte[] getDNDPolicyProto() {
561             if (Flags.modesApi() && mNewZenMode == ZEN_MODE_OFF) {
562                 return null;
563             }
564 
565             ByteArrayOutputStream bytes = new ByteArrayOutputStream();
566             ProtoOutputStream proto = new ProtoOutputStream(bytes);
567 
568             // While we don't expect this to be null at any point, guard against any weird cases.
569             if (mNewPolicy != null) {
570                 proto.write(DNDPolicyProto.CALLS, toState(mNewPolicy.allowCalls()));
571                 proto.write(DNDPolicyProto.REPEAT_CALLERS,
572                         toState(mNewPolicy.allowRepeatCallers()));
573                 proto.write(DNDPolicyProto.MESSAGES, toState(mNewPolicy.allowMessages()));
574                 proto.write(DNDPolicyProto.CONVERSATIONS, toState(mNewPolicy.allowConversations()));
575                 proto.write(DNDPolicyProto.REMINDERS, toState(mNewPolicy.allowReminders()));
576                 proto.write(DNDPolicyProto.EVENTS, toState(mNewPolicy.allowEvents()));
577                 proto.write(DNDPolicyProto.ALARMS, toState(mNewPolicy.allowAlarms()));
578                 proto.write(DNDPolicyProto.MEDIA, toState(mNewPolicy.allowMedia()));
579                 proto.write(DNDPolicyProto.SYSTEM, toState(mNewPolicy.allowSystem()));
580 
581                 proto.write(DNDPolicyProto.FULLSCREEN, toState(mNewPolicy.showFullScreenIntents()));
582                 proto.write(DNDPolicyProto.LIGHTS, toState(mNewPolicy.showLights()));
583                 proto.write(DNDPolicyProto.PEEK, toState(mNewPolicy.showPeeking()));
584                 proto.write(DNDPolicyProto.STATUS_BAR, toState(mNewPolicy.showStatusBarIcons()));
585                 proto.write(DNDPolicyProto.BADGE, toState(mNewPolicy.showBadges()));
586                 proto.write(DNDPolicyProto.AMBIENT, toState(mNewPolicy.showAmbient()));
587                 proto.write(DNDPolicyProto.NOTIFICATION_LIST,
588                         toState(mNewPolicy.showInNotificationList()));
589 
590                 // Note: The DND policy proto uses the people type enum from *ZenPolicy* and not
591                 // *NotificationManager.Policy* (which is the type of the consolidated policy).
592                 // This applies to both call and message senders, but not conversation senders,
593                 // where they use the same enum values.
594                 proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
595                         ZenAdapters.prioritySendersToPeopleType(
596                                 mNewPolicy.allowCallsFrom()));
597                 proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
598                         ZenAdapters.prioritySendersToPeopleType(
599                                 mNewPolicy.allowMessagesFrom()));
600                 proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
601                         mNewPolicy.allowConversationsFrom());
602 
603                 if (Flags.modesApi()) {
604                     proto.write(DNDPolicyProto.ALLOW_CHANNELS,
605                             mNewPolicy.allowPriorityChannels()
606                                     ? CHANNEL_POLICY_PRIORITY
607                                     : CHANNEL_POLICY_NONE);
608                 }
609             } else {
610                 Log.wtf(TAG, "attempted to write zen mode log event with null policy");
611             }
612 
613             proto.flush();
614             return bytes.toByteArray();
615         }
616 
617         /**
618          * Get whether any channels are bypassing DND based on the current new policy.
619          */
getAreChannelsBypassing()620         boolean getAreChannelsBypassing() {
621             if (mNewPolicy != null) {
622                 return (mNewPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0;
623             }
624             return false;
625         }
626 
hasChannelsBypassingDiff()627         private boolean hasChannelsBypassingDiff() {
628             boolean prevChannelsBypassing = mPrevPolicy != null
629                     ? (mPrevPolicy.state & STATE_CHANNELS_BYPASSING_DND) != 0 : false;
630             return prevChannelsBypassing != getAreChannelsBypassing();
631         }
632 
633         /**
634          * helper method to turn a boolean allow or disallow state into STATE_ALLOW or
635          * STATE_DISALLOW (there is no concept of "unset" in NM.Policy.)
636          */
toState(boolean allow)637         private int toState(boolean allow) {
638             return allow ? ZenPolicy.STATE_ALLOW : ZenPolicy.STATE_DISALLOW;
639         }
640 
641         /**
642          * Get the list of automatic rules that have any diff (as a List of ZenModeDiff.RuleDiff).
643          * Returns an empty list if there isn't anything.
644          */
getChangedAutomaticRules()645         private @NonNull ArrayMap<String, ZenModeDiff.RuleDiff> getChangedAutomaticRules() {
646             ArrayMap<String, ZenModeDiff.RuleDiff> ruleDiffs = new ArrayMap<>();
647 
648             ZenModeDiff.ConfigDiff diff = new ZenModeDiff.ConfigDiff(mPrevConfig, mNewConfig);
649             if (!diff.hasDiff()) {
650                 return ruleDiffs;
651             }
652 
653             ArrayMap<String, ZenModeDiff.RuleDiff> autoDiffs = diff.getAllAutomaticRuleDiffs();
654             if (autoDiffs != null) {
655                 return autoDiffs;
656             }
657             return ruleDiffs;
658         }
659 
660         /**
661          * Get the package name associated with this rule's owner, given its id and associated
662          * RuleDiff, as well as the user ID associated with the config it was found in. Returns null
663          * if none could be found.
664          */
getRulePackageAndUser(String id, ZenModeDiff.RuleDiff diff)665         private Pair<String, Integer> getRulePackageAndUser(String id, ZenModeDiff.RuleDiff diff) {
666             // look for the rule info in the new config unless the rule was deleted.
667             ZenModeConfig configForSearch = mNewConfig;
668             if (diff.wasRemoved()) {
669                 configForSearch = mPrevConfig;
670             }
671 
672             if (configForSearch == null) {
673                 return null;
674             }
675 
676             ZenModeConfig.ZenRule rule = configForSearch.automaticRules.getOrDefault(id, null);
677             if (rule != null) {
678                 if (rule.component != null) {
679                     return new Pair(rule.component.getPackageName(), configForSearch.user);
680                 }
681                 if (rule.configurationActivity != null) {
682                     return new Pair(rule.configurationActivity.getPackageName(),
683                             configForSearch.user);
684                 }
685             }
686             return null;
687         }
688 
689         /**
690          * Get the package name listed as the manual rule "enabler", if it exists in the new config.
691          */
getNewManualRuleEnabler()692         private String getNewManualRuleEnabler() {
693             if (mNewConfig == null || mNewConfig.manualRule == null) {
694                 return null;
695             }
696             return mNewConfig.manualRule.enabler;
697         }
698 
699         /**
700          * Makes a copy for storing intermediate state for testing purposes.
701          */
copy()702         protected ZenStateChanges copy() {
703             ZenStateChanges copy = new ZenStateChanges();
704             copy.mPrevZenMode = mPrevZenMode;
705             copy.mNewZenMode = mNewZenMode;
706             copy.mPrevConfig = mPrevConfig.copy();
707             copy.mNewConfig = mNewConfig.copy();
708             copy.mPrevPolicy = mPrevPolicy.copy();
709             copy.mNewPolicy = mNewPolicy.copy();
710             copy.mCallingUid = mCallingUid;
711             copy.mOrigin = mOrigin;
712             return copy;
713         }
714     }
715 }
716