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