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