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