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