1 /** 2 * Copyright (c) 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, 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 android.service.notification; 18 19 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; 20 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; 21 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; 22 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 23 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; 25 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 26 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 27 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 28 import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders; 29 import static android.service.notification.ZenAdapters.prioritySendersToPeopleType; 30 import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy; 31 import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE; 32 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; 33 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS; 34 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS; 35 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS; 36 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA; 37 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES; 38 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS; 39 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS; 40 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM; 41 import static android.service.notification.ZenPolicy.STATE_ALLOW; 42 import static android.service.notification.ZenPolicy.STATE_DISALLOW; 43 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT; 44 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT; 45 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; 46 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK; 47 48 import android.annotation.FlaggedApi; 49 import android.annotation.IntDef; 50 import android.annotation.NonNull; 51 import android.annotation.Nullable; 52 import android.app.ActivityManager; 53 import android.app.AlarmManager; 54 import android.app.AutomaticZenRule; 55 import android.app.Flags; 56 import android.app.NotificationManager; 57 import android.app.NotificationManager.Policy; 58 import android.compat.annotation.UnsupportedAppUsage; 59 import android.content.ComponentName; 60 import android.content.Context; 61 import android.content.pm.ApplicationInfo; 62 import android.content.pm.PackageManager; 63 import android.content.res.Resources; 64 import android.net.Uri; 65 import android.os.Build; 66 import android.os.Parcel; 67 import android.os.Parcelable; 68 import android.os.UserHandle; 69 import android.provider.Settings.Global; 70 import android.text.TextUtils; 71 import android.text.format.DateFormat; 72 import android.util.ArrayMap; 73 import android.util.PluralsMessageFormatter; 74 import android.util.Slog; 75 import android.util.proto.ProtoOutputStream; 76 77 import com.android.internal.R; 78 import com.android.internal.annotations.VisibleForTesting; 79 import com.android.internal.util.XmlUtils; 80 import com.android.modules.utils.TypedXmlPullParser; 81 import com.android.modules.utils.TypedXmlSerializer; 82 83 import org.xmlpull.v1.XmlPullParser; 84 import org.xmlpull.v1.XmlPullParserException; 85 86 import java.io.IOException; 87 import java.lang.annotation.Retention; 88 import java.lang.annotation.RetentionPolicy; 89 import java.time.Instant; 90 import java.util.ArrayList; 91 import java.util.Arrays; 92 import java.util.Calendar; 93 import java.util.Date; 94 import java.util.GregorianCalendar; 95 import java.util.HashMap; 96 import java.util.HashSet; 97 import java.util.List; 98 import java.util.Locale; 99 import java.util.Map; 100 import java.util.Objects; 101 import java.util.Set; 102 import java.util.TimeZone; 103 import java.util.UUID; 104 import java.util.regex.Pattern; 105 106 /** 107 * Persisted configuration for zen mode. 108 * 109 * @hide 110 */ 111 public class ZenModeConfig implements Parcelable { 112 private static final String TAG = "ZenModeConfig"; 113 114 /** 115 * The {@link ZenModeConfig} is being updated because of an unknown reason. 116 */ 117 public static final int UPDATE_ORIGIN_UNKNOWN = 0; 118 119 /** 120 * The {@link ZenModeConfig} is being updated because of system initialization (i.e. load from 121 * storage, on device boot). 122 */ 123 public static final int UPDATE_ORIGIN_INIT = 1; 124 125 /** The {@link ZenModeConfig} is being updated (replaced) because of a user switch or unlock. */ 126 public static final int UPDATE_ORIGIN_INIT_USER = 2; 127 128 /** The {@link ZenModeConfig} is being updated because of a user action, for example: 129 * <ul> 130 * <li>{@link NotificationManager#setAutomaticZenRuleState} with a 131 * {@link Condition#source} equal to {@link Condition#SOURCE_USER_ACTION}.</li> 132 * <li>Adding, updating, or removing a rule from Settings.</li> 133 * <li>Directly activating or deactivating/snoozing a rule through some UI affordance (e.g. 134 * Quick Settings).</li> 135 * </ul> 136 */ 137 public static final int UPDATE_ORIGIN_USER = 3; 138 139 /** 140 * The {@link ZenModeConfig} is being "independently" updated by an app, and not as a result of 141 * a user's action inside that app (for example, activating an {@link AutomaticZenRule} based on 142 * a previously set schedule). 143 */ 144 public static final int UPDATE_ORIGIN_APP = 4; 145 146 /** 147 * The {@link ZenModeConfig} is being updated by the System or SystemUI. Note that this only 148 * includes cases where the call is coming from the System/SystemUI but the change is not due to 149 * a user action (e.g. automatically activating a schedule-based rule). If the change is a 150 * result of a user action (e.g. activating a rule by tapping on its QS tile) then 151 * {@link #UPDATE_ORIGIN_USER} is used instead. 152 */ 153 public static final int UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI = 5; 154 155 /** 156 * The {@link ZenModeConfig} is being updated (replaced) because the user's DND configuration 157 * is being restored from a backup. 158 */ 159 public static final int UPDATE_ORIGIN_RESTORE_BACKUP = 6; 160 161 @IntDef(prefix = { "UPDATE_ORIGIN_" }, value = { 162 UPDATE_ORIGIN_UNKNOWN, 163 UPDATE_ORIGIN_INIT, 164 UPDATE_ORIGIN_INIT_USER, 165 UPDATE_ORIGIN_USER, 166 UPDATE_ORIGIN_APP, 167 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, 168 UPDATE_ORIGIN_RESTORE_BACKUP 169 }) 170 @Retention(RetentionPolicy.SOURCE) 171 public @interface ConfigChangeOrigin {} 172 173 public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY; 174 public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS; 175 public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED; 176 public static final int MAX_SOURCE = SOURCE_STAR; 177 private static final int DEFAULT_SOURCE = SOURCE_STAR; 178 private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR; 179 180 public static final String MANUAL_RULE_ID = "MANUAL_RULE"; 181 public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE"; 182 public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE"; 183 public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID, 184 EVENTS_DEFAULT_RULE_ID); 185 186 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, 187 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; 188 189 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets(); 190 private static final int SECONDS_MS = 1000; 191 private static final int MINUTES_MS = 60 * SECONDS_MS; 192 private static final int DAY_MINUTES = 24 * 60; 193 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS; 194 195 // Default allow categories set in readXml() from default_zen_mode_config.xml, 196 // fallback/upgrade values: 197 private static final boolean DEFAULT_ALLOW_ALARMS = true; 198 private static final boolean DEFAULT_ALLOW_MEDIA = true; 199 private static final boolean DEFAULT_ALLOW_SYSTEM = false; 200 private static final boolean DEFAULT_ALLOW_CALLS = true; 201 private static final boolean DEFAULT_ALLOW_MESSAGES = true; 202 private static final boolean DEFAULT_ALLOW_REMINDERS = false; 203 private static final boolean DEFAULT_ALLOW_EVENTS = false; 204 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true; 205 private static final boolean DEFAULT_ALLOW_CONV = true; 206 private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; 207 private static final boolean DEFAULT_ALLOW_PRIORITY_CHANNELS = true; 208 private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false; 209 // Default setting here is 010011101 = 157 210 private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 211 SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT 212 | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT; 213 214 // ZenModeConfig XML versions distinguishing key changes. 215 public static final int XML_VERSION_ZEN_UPGRADE = 8; 216 public static final int XML_VERSION_MODES_API = 11; 217 218 // TODO: b/310620812 - Update XML_VERSION and update default_zen_config.xml accordingly when 219 // modes_api is inlined. 220 private static final int XML_VERSION = 10; 221 public static final String ZEN_TAG = "zen"; 222 private static final String ZEN_ATT_VERSION = "version"; 223 private static final String ZEN_ATT_USER = "user"; 224 private static final String ALLOW_TAG = "allow"; 225 private static final String ALLOW_ATT_ALARMS = "alarms"; 226 private static final String ALLOW_ATT_MEDIA = "media"; 227 private static final String ALLOW_ATT_SYSTEM = "system"; 228 private static final String ALLOW_ATT_CALLS = "calls"; 229 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers"; 230 private static final String ALLOW_ATT_MESSAGES = "messages"; 231 private static final String ALLOW_ATT_FROM = "from"; 232 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom"; 233 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom"; 234 private static final String ALLOW_ATT_REMINDERS = "reminders"; 235 private static final String ALLOW_ATT_EVENTS = "events"; 236 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff"; 237 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn"; 238 private static final String ALLOW_ATT_CONV = "convos"; 239 private static final String ALLOW_ATT_CONV_FROM = "convosFrom"; 240 private static final String ALLOW_ATT_CHANNELS = "priorityChannelsAllowed"; 241 private static final String POLICY_USER_MODIFIED_FIELDS = "policyUserModifiedFields"; 242 private static final String DISALLOW_TAG = "disallow"; 243 private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects"; 244 private static final String STATE_TAG = "state"; 245 private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd"; 246 247 // zen policy visual effects attributes 248 private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent"; 249 private static final String SHOW_ATT_LIGHTS = "showLights"; 250 private static final String SHOW_ATT_PEEK = "shoePeek"; 251 private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons"; 252 private static final String SHOW_ATT_BADGES = "showBadges"; 253 private static final String SHOW_ATT_AMBIENT = "showAmbient"; 254 private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList"; 255 256 private static final String CONDITION_ATT_ID = "id"; 257 private static final String CONDITION_ATT_SUMMARY = "summary"; 258 private static final String CONDITION_ATT_LINE1 = "line1"; 259 private static final String CONDITION_ATT_LINE2 = "line2"; 260 private static final String CONDITION_ATT_ICON = "icon"; 261 private static final String CONDITION_ATT_STATE = "state"; 262 private static final String CONDITION_ATT_SOURCE = "source"; 263 private static final String CONDITION_ATT_FLAGS = "flags"; 264 265 private static final String ZEN_POLICY_TAG = "zen_policy"; 266 267 private static final String MANUAL_TAG = "manual"; 268 private static final String AUTOMATIC_TAG = "automatic"; 269 private static final String AUTOMATIC_DELETED_TAG = "deleted"; 270 271 private static final String RULE_ATT_ID = "ruleId"; 272 private static final String RULE_ATT_ENABLED = "enabled"; 273 private static final String RULE_ATT_SNOOZING = "snoozing"; 274 private static final String RULE_ATT_NAME = "name"; 275 private static final String RULE_ATT_PKG = "pkg"; 276 private static final String RULE_ATT_COMPONENT = "component"; 277 private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity"; 278 private static final String RULE_ATT_ZEN = "zen"; 279 private static final String RULE_ATT_CONDITION_ID = "conditionId"; 280 private static final String RULE_ATT_CREATION_TIME = "creationTime"; 281 private static final String RULE_ATT_ENABLER = "enabler"; 282 private static final String RULE_ATT_MODIFIED = "modified"; 283 private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable"; 284 private static final String RULE_ATT_TYPE = "type"; 285 private static final String RULE_ATT_USER_MODIFIED_FIELDS = "userModifiedFields"; 286 private static final String RULE_ATT_ICON = "rule_icon"; 287 private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc"; 288 private static final String RULE_ATT_DELETION_INSTANT = "deletionInstant"; 289 290 private static final String DEVICE_EFFECT_DISPLAY_GRAYSCALE = "zdeDisplayGrayscale"; 291 private static final String DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY = 292 "zdeSuppressAmbientDisplay"; 293 private static final String DEVICE_EFFECT_DIM_WALLPAPER = "zdeDimWallpaper"; 294 private static final String DEVICE_EFFECT_USE_NIGHT_MODE = "zdeUseNightMode"; 295 private static final String DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS = "zdeDisableAutoBrightness"; 296 private static final String DEVICE_EFFECT_DISABLE_TAP_TO_WAKE = "zdeDisableTapToWake"; 297 private static final String DEVICE_EFFECT_DISABLE_TILT_TO_WAKE = "zdeDisableTiltToWake"; 298 private static final String DEVICE_EFFECT_DISABLE_TOUCH = "zdeDisableTouch"; 299 private static final String DEVICE_EFFECT_MINIMIZE_RADIO_USAGE = "zdeMinimizeRadioUsage"; 300 private static final String DEVICE_EFFECT_MAXIMIZE_DOZE = "zdeMaximizeDoze"; 301 private static final String DEVICE_EFFECT_EXTRAS = "zdeExtraEffects"; 302 private static final String DEVICE_EFFECT_USER_MODIFIED_FIELDS = "zdeUserModifiedFields"; 303 304 private static final String ITEM_SEPARATOR = ","; 305 private static final String ITEM_SEPARATOR_ESCAPE = "\\"; 306 private static final Pattern ITEM_SPLITTER_REGEX = Pattern.compile("(?<!\\\\),"); 307 308 @UnsupportedAppUsage 309 public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; 310 public boolean allowMedia = DEFAULT_ALLOW_MEDIA; 311 public boolean allowSystem = DEFAULT_ALLOW_SYSTEM; 312 public boolean allowCalls = DEFAULT_ALLOW_CALLS; 313 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; 314 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES; 315 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS; 316 public boolean allowEvents = DEFAULT_ALLOW_EVENTS; 317 public int allowCallsFrom = DEFAULT_CALLS_SOURCE; 318 public int allowMessagesFrom = DEFAULT_SOURCE; 319 public boolean allowConversations = DEFAULT_ALLOW_CONV; 320 public int allowConversationsFrom = DEFAULT_ALLOW_CONV_FROM; 321 public int user = UserHandle.USER_SYSTEM; 322 public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS; 323 // Note that when the modes_api flag is true, the areChannelsBypassingDnd boolean only tracks 324 // whether the current user has any priority channels. These channels may bypass DND when 325 // allowPriorityChannels is true. 326 // TODO: b/310620812 - Rename to be more accurate when modes_api flag is inlined. 327 public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND; 328 public boolean allowPriorityChannels = DEFAULT_ALLOW_PRIORITY_CHANNELS; 329 public int version; 330 331 public ZenRule manualRule; 332 @UnsupportedAppUsage 333 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); 334 335 // Note: Map is *pkg|conditionId* (see deletedRuleKey()) -> ZenRule, 336 // unlike automaticRules (which is id -> rule). 337 public final ArrayMap<String, ZenRule> deletedRules = new ArrayMap<>(); 338 339 @UnsupportedAppUsage ZenModeConfig()340 public ZenModeConfig() { 341 if (Flags.modesUi()) { 342 ensureManualZenRule(); 343 } 344 } 345 ZenModeConfig(Parcel source)346 public ZenModeConfig(Parcel source) { 347 if (!Flags.modesUi()) { 348 allowCalls = source.readInt() == 1; 349 allowRepeatCallers = source.readInt() == 1; 350 allowMessages = source.readInt() == 1; 351 allowReminders = source.readInt() == 1; 352 allowEvents = source.readInt() == 1; 353 allowCallsFrom = source.readInt(); 354 allowMessagesFrom = source.readInt(); 355 } 356 user = source.readInt(); 357 manualRule = source.readParcelable(null, ZenRule.class); 358 readRulesFromParcel(automaticRules, source); 359 if (Flags.modesApi()) { 360 readRulesFromParcel(deletedRules, source); 361 } 362 if (!Flags.modesUi()) { 363 allowAlarms = source.readInt() == 1; 364 allowMedia = source.readInt() == 1; 365 allowSystem = source.readInt() == 1; 366 suppressedVisualEffects = source.readInt(); 367 } 368 areChannelsBypassingDnd = source.readInt() == 1; 369 if (!Flags.modesUi()) { 370 allowConversations = source.readBoolean(); 371 allowConversationsFrom = source.readInt(); 372 if (Flags.modesApi()) { 373 allowPriorityChannels = source.readBoolean(); 374 } 375 } 376 } 377 getDefaultZenPolicy()378 public static ZenPolicy getDefaultZenPolicy() { 379 ZenPolicy policy = new ZenPolicy.Builder() 380 .allowAlarms(true) 381 .allowMedia(true) 382 .allowSystem(false) 383 .allowCalls(PEOPLE_TYPE_STARRED) 384 .allowMessages(PEOPLE_TYPE_STARRED) 385 .allowReminders(false) 386 .allowEvents(false) 387 .allowRepeatCallers(true) 388 .allowConversations(CONVERSATION_SENDERS_IMPORTANT) 389 .showAllVisualEffects() 390 .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false) 391 .showVisualEffect(VISUAL_EFFECT_LIGHTS, false) 392 .showVisualEffect(VISUAL_EFFECT_PEEK, false) 393 .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) 394 .allowPriorityChannels(true) 395 .build(); 396 return policy; 397 } 398 getDefaultConfig()399 public static ZenModeConfig getDefaultConfig() { 400 ZenModeConfig config = new ZenModeConfig(); 401 402 EventInfo eventInfo = new EventInfo(); 403 eventInfo.reply = REPLY_YES_OR_MAYBE; 404 ZenRule events = new ZenRule(); 405 events.id = EVENTS_DEFAULT_RULE_ID; 406 events.conditionId = toEventConditionId(eventInfo); 407 events.component = ComponentName.unflattenFromString( 408 "android/com.android.server.notification.EventConditionProvider"); 409 events.enabled = false; 410 events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; 411 events.pkg = "android"; 412 config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events); 413 414 ScheduleInfo scheduleInfo = new ScheduleInfo(); 415 scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7}; 416 scheduleInfo.startHour = 22; 417 scheduleInfo.endHour = 7; 418 scheduleInfo.exitAtAlarm = true; 419 ZenRule sleeping = new ZenRule(); 420 sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID; 421 sleeping.conditionId = toScheduleConditionId(scheduleInfo); 422 sleeping.component = ComponentName.unflattenFromString( 423 "android/com.android.server.notification.ScheduleConditionProvider"); 424 sleeping.enabled = false; 425 sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; 426 sleeping.pkg = "android"; 427 config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping); 428 429 return config; 430 } 431 ensureManualZenRule()432 void ensureManualZenRule() { 433 if (manualRule == null) { 434 final ZenRule newRule = new ZenRule(); 435 newRule.type = AutomaticZenRule.TYPE_OTHER; 436 newRule.enabled = true; 437 newRule.conditionId = Uri.EMPTY; 438 newRule.allowManualInvocation = true; 439 newRule.zenPolicy = getDefaultZenPolicy(); 440 newRule.pkg = "android"; 441 manualRule = newRule; 442 } 443 } 444 readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source)445 private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) { 446 final int len = source.readInt(); 447 if (len > 0) { 448 final String[] ids = new String[len]; 449 final ZenRule[] rules = new ZenRule[len]; 450 source.readStringArray(ids); 451 source.readTypedArray(rules, ZenRule.CREATOR); 452 for (int i = 0; i < len; i++) { 453 ruleMap.put(ids[i], rules[i]); 454 } 455 } 456 } 457 458 @Override writeToParcel(Parcel dest, int flags)459 public void writeToParcel(Parcel dest, int flags) { 460 if (!Flags.modesUi()) { 461 dest.writeInt(allowCalls ? 1 : 0); 462 dest.writeInt(allowRepeatCallers ? 1 : 0); 463 dest.writeInt(allowMessages ? 1 : 0); 464 dest.writeInt(allowReminders ? 1 : 0); 465 dest.writeInt(allowEvents ? 1 : 0); 466 dest.writeInt(allowCallsFrom); 467 dest.writeInt(allowMessagesFrom); 468 } 469 dest.writeInt(user); 470 dest.writeParcelable(manualRule, 0); 471 writeRulesToParcel(automaticRules, dest); 472 if (Flags.modesApi()) { 473 writeRulesToParcel(deletedRules, dest); 474 } 475 if (!Flags.modesUi()) { 476 dest.writeInt(allowAlarms ? 1 : 0); 477 dest.writeInt(allowMedia ? 1 : 0); 478 dest.writeInt(allowSystem ? 1 : 0); 479 dest.writeInt(suppressedVisualEffects); 480 } 481 dest.writeInt(areChannelsBypassingDnd ? 1 : 0); 482 if (!Flags.modesUi()) { 483 dest.writeBoolean(allowConversations); 484 dest.writeInt(allowConversationsFrom); 485 if (Flags.modesApi()) { 486 dest.writeBoolean(allowPriorityChannels); 487 } 488 } 489 } 490 writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest)491 private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) { 492 if (!ruleMap.isEmpty()) { 493 final int len = ruleMap.size(); 494 final String[] ids = new String[len]; 495 final ZenRule[] rules = new ZenRule[len]; 496 for (int i = 0; i < len; i++) { 497 ids[i] = ruleMap.keyAt(i); 498 rules[i] = ruleMap.valueAt(i); 499 } 500 dest.writeInt(len); 501 dest.writeStringArray(ids); 502 dest.writeTypedArray(rules, 0); 503 } else { 504 dest.writeInt(0); 505 } 506 } 507 508 @Override toString()509 public String toString() { 510 StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') 511 .append("user=").append(user); 512 if (!Flags.modesUi()) { 513 sb.append(",allowAlarms=").append(allowAlarms) 514 .append(",allowMedia=").append(allowMedia) 515 .append(",allowSystem=").append(allowSystem) 516 .append(",allowReminders=").append(allowReminders) 517 .append(",allowEvents=").append(allowEvents) 518 .append(",allowCalls=").append(allowCalls) 519 .append(",allowRepeatCallers=").append(allowRepeatCallers) 520 .append(",allowMessages=").append(allowMessages) 521 .append(",allowConversations=").append(allowConversations) 522 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) 523 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) 524 .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString 525 (allowConversationsFrom)) 526 .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects); 527 } 528 if (Flags.modesApi()) { 529 sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd); 530 sb.append(",allowPriorityChannels=").append(allowPriorityChannels); 531 } else { 532 sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd); 533 } 534 sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)); 535 sb.append(",\nmanualRule=").append(manualRule); 536 if (Flags.modesApi()) { 537 sb.append(",\ndeletedRules=").append(rulesToString(deletedRules)); 538 } 539 return sb.append(']').toString(); 540 } 541 isAllowPriorityChannels()542 public boolean isAllowPriorityChannels() { 543 if (Flags.modesUi()) { 544 throw new IllegalStateException("can't be used with modesUI flag"); 545 } 546 return allowPriorityChannels; 547 } 548 setAllowPriorityChannels(boolean allowPriorityChannels)549 public void setAllowPriorityChannels(boolean allowPriorityChannels) { 550 if (Flags.modesUi()) { 551 throw new IllegalStateException("can't be used with modesUI flag"); 552 } else { 553 this.allowPriorityChannels = allowPriorityChannels; 554 } 555 } 556 getSuppressedVisualEffects()557 public int getSuppressedVisualEffects() { 558 if (Flags.modesUi()) { 559 throw new IllegalStateException("can't be used with modesUI flag"); 560 } else { 561 return this.suppressedVisualEffects; 562 } 563 } 564 setSuppressedVisualEffects(int suppressedVisualEffects)565 public void setSuppressedVisualEffects(int suppressedVisualEffects) { 566 if (Flags.modesUi()) { 567 throw new IllegalStateException("can't be used with modesUI flag"); 568 } else { 569 this.suppressedVisualEffects = suppressedVisualEffects; 570 } 571 } 572 getAllowConversationsFrom()573 public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() { 574 if (Flags.modesUi()) { 575 return manualRule.zenPolicy.getPriorityConversationSenders(); 576 } 577 return allowConversationsFrom; 578 } 579 setAllowConversationsFrom( @enPolicy.ConversationSenders int allowConversationsFrom)580 public void setAllowConversationsFrom( 581 @ZenPolicy.ConversationSenders int allowConversationsFrom) { 582 if (Flags.modesUi()) { 583 throw new IllegalStateException("can't be used with modesUI flag"); 584 } else { 585 this.allowConversationsFrom = allowConversationsFrom; 586 } 587 } 588 setAllowConversations(boolean allowConversations)589 public void setAllowConversations(boolean allowConversations) { 590 if (Flags.modesUi()) { 591 throw new IllegalStateException("can't be used with modesUI flag"); 592 } else { 593 this.allowConversations = allowConversations; 594 } 595 } 596 isAllowConversations()597 public boolean isAllowConversations() { 598 if (Flags.modesUi()) { 599 throw new IllegalStateException("can't be used with modesUI flag"); 600 } 601 return allowConversations; 602 } 603 getAllowMessagesFrom()604 public @Policy.PrioritySenders int getAllowMessagesFrom() { 605 if (Flags.modesUi()) { 606 throw new IllegalStateException("can't be used with modesUI flag"); 607 } 608 return allowMessagesFrom; 609 } 610 setAllowMessagesFrom(@olicy.PrioritySenders int allowMessagesFrom)611 public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) { 612 if (Flags.modesUi()) { 613 throw new IllegalStateException("can't be used with modesUI flag"); 614 } else { 615 this.allowMessagesFrom = allowMessagesFrom; 616 } 617 } 618 setAllowMessages(boolean allowMessages)619 public void setAllowMessages(boolean allowMessages) { 620 if (Flags.modesUi()) { 621 throw new IllegalStateException("can't be used with modesUI flag"); 622 } 623 this.allowMessages = allowMessages; 624 } 625 getAllowCallsFrom()626 public @Policy.PrioritySenders int getAllowCallsFrom() { 627 if (Flags.modesUi()) { 628 return peopleTypeToPrioritySenders( 629 manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); 630 } 631 return allowCallsFrom; 632 } 633 setAllowCallsFrom(@olicy.PrioritySenders int allowCallsFrom)634 public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) { 635 if (Flags.modesUi()) { 636 manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy) 637 .allowCalls(prioritySendersToPeopleType(allowCallsFrom)) 638 .build(); 639 } else { 640 this.allowCallsFrom = allowCallsFrom; 641 } 642 } 643 setAllowCalls(boolean allowCalls)644 public void setAllowCalls(boolean allowCalls) { 645 if (Flags.modesUi()) { 646 throw new IllegalStateException("can't be used with modesUI flag"); 647 } 648 this.allowCalls = allowCalls; 649 } 650 isAllowEvents()651 public boolean isAllowEvents() { 652 if (Flags.modesUi()) { 653 return manualRule.zenPolicy.isCategoryAllowed( 654 ZenPolicy.PRIORITY_CATEGORY_EVENTS, false); 655 } 656 return allowEvents; 657 } 658 setAllowEvents(boolean allowEvents)659 public void setAllowEvents(boolean allowEvents) { 660 if (Flags.modesUi()) { 661 throw new IllegalStateException("can't be used with modesUI flag"); 662 } else { 663 this.allowEvents = allowEvents; 664 } 665 } 666 isAllowReminders()667 public boolean isAllowReminders() { 668 if (Flags.modesUi()) { 669 throw new IllegalStateException("can't be used with modesUI flag"); 670 } 671 return allowReminders; 672 } 673 setAllowReminders(boolean allowReminders)674 public void setAllowReminders(boolean allowReminders) { 675 if (Flags.modesUi()) { 676 throw new IllegalStateException("can't be used with modesUI flag"); 677 } else { 678 this.allowReminders = allowReminders; 679 } 680 } 681 isAllowMessages()682 public boolean isAllowMessages() { 683 if (Flags.modesUi()) { 684 throw new IllegalStateException("can't be used with modesUI flag"); 685 } 686 return allowMessages; 687 } 688 isAllowRepeatCallers()689 public boolean isAllowRepeatCallers() { 690 if (Flags.modesUi()) { 691 throw new IllegalStateException("can't be used with modesUI flag"); 692 } 693 return allowRepeatCallers; 694 } 695 setAllowRepeatCallers(boolean allowRepeatCallers)696 public void setAllowRepeatCallers(boolean allowRepeatCallers) { 697 if (Flags.modesUi()) { 698 throw new IllegalStateException("can't be used with modesUI flag"); 699 } else { 700 this.allowRepeatCallers = allowRepeatCallers; 701 } 702 } 703 isAllowSystem()704 public boolean isAllowSystem() { 705 if (Flags.modesUi()) { 706 throw new IllegalStateException("can't be used with modesUI flag"); 707 } 708 return allowSystem; 709 } 710 setAllowSystem(boolean allowSystem)711 public void setAllowSystem(boolean allowSystem) { 712 if (Flags.modesUi()) { 713 throw new IllegalStateException("can't be used with modesUI flag"); 714 } else { 715 this.allowSystem = allowSystem; 716 } 717 } 718 isAllowMedia()719 public boolean isAllowMedia() { 720 if (Flags.modesUi()) { 721 throw new IllegalStateException("can't be used with modesUI flag"); 722 } 723 return allowMedia; 724 } 725 setAllowMedia(boolean allowMedia)726 public void setAllowMedia(boolean allowMedia) { 727 if (Flags.modesUi()) { 728 throw new IllegalStateException("can't be used with modesUI flag"); 729 } else { 730 this.allowMedia = allowMedia; 731 } 732 } 733 isAllowAlarms()734 public boolean isAllowAlarms() { 735 if (Flags.modesUi()) { 736 throw new IllegalStateException("can't be used with modesUI flag"); 737 } 738 return allowAlarms; 739 } 740 setAllowAlarms(boolean allowAlarms)741 public void setAllowAlarms(boolean allowAlarms) { 742 if (Flags.modesUi()) { 743 throw new IllegalStateException("can't be used with modesUI flag"); 744 } else { 745 this.allowAlarms = allowAlarms; 746 } 747 } 748 isAllowCalls()749 public boolean isAllowCalls() { 750 if (Flags.modesUi()) { 751 throw new IllegalStateException("can't be used with modesUI flag"); 752 } 753 return allowCalls; 754 } 755 rulesToString(ArrayMap<String, ZenRule> ruleList)756 private static String rulesToString(ArrayMap<String, ZenRule> ruleList) { 757 if (ruleList.isEmpty()) { 758 return "{}"; 759 } 760 761 StringBuilder buffer = new StringBuilder(ruleList.size() * 28); 762 buffer.append("{\n"); 763 for (int i = 0; i < ruleList.size(); i++) { 764 if (i > 0) { 765 buffer.append(",\n"); 766 } 767 Object value = ruleList.valueAt(i); 768 buffer.append(value); 769 } 770 buffer.append('}'); 771 return buffer.toString(); 772 } 773 isValid()774 public boolean isValid() { 775 if (!isValidManualRule(manualRule)) return false; 776 final int N = automaticRules.size(); 777 for (int i = 0; i < N; i++) { 778 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false; 779 } 780 return true; 781 } 782 isValidManualRule(ZenRule rule)783 private static boolean isValidManualRule(ZenRule rule) { 784 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule); 785 } 786 isValidAutomaticRule(ZenRule rule)787 private static boolean isValidAutomaticRule(ZenRule rule) { 788 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode) 789 && rule.conditionId != null && sameCondition(rule); 790 } 791 sameCondition(ZenRule rule)792 private static boolean sameCondition(ZenRule rule) { 793 if (rule == null) return false; 794 if (rule.conditionId == null) { 795 return rule.condition == null; 796 } else { 797 return rule.condition == null || rule.conditionId.equals(rule.condition.id); 798 } 799 } 800 generateMinuteBuckets()801 private static int[] generateMinuteBuckets() { 802 final int maxHrs = 12; 803 final int[] buckets = new int[maxHrs + 3]; 804 buckets[0] = 15; 805 buckets[1] = 30; 806 buckets[2] = 45; 807 for (int i = 1; i <= maxHrs; i++) { 808 buckets[2 + i] = 60 * i; 809 } 810 return buckets; 811 } 812 sourceToString(int source)813 public static String sourceToString(int source) { 814 switch (source) { 815 case SOURCE_ANYONE: 816 return "anyone"; 817 case SOURCE_CONTACT: 818 return "contacts"; 819 case SOURCE_STAR: 820 return "stars"; 821 default: 822 return "UNKNOWN"; 823 } 824 } 825 826 @Override equals(@ullable Object o)827 public boolean equals(@Nullable Object o) { 828 if (!(o instanceof ZenModeConfig)) return false; 829 if (o == this) return true; 830 final ZenModeConfig other = (ZenModeConfig) o; 831 // The policy fields that live on config are compared directly because the fields will 832 // contain data until MODES_UI is rolled out/cleaned up. 833 boolean eq = other.allowAlarms == allowAlarms 834 && other.allowMedia == allowMedia 835 && other.allowSystem == allowSystem 836 && other.allowCalls == allowCalls 837 && other.allowRepeatCallers == allowRepeatCallers 838 && other.allowMessages == allowMessages 839 && other.allowCallsFrom == allowCallsFrom 840 && other.allowMessagesFrom == allowMessagesFrom 841 && other.allowReminders == allowReminders 842 && other.allowEvents == allowEvents 843 && other.user == user 844 && Objects.equals(other.automaticRules, automaticRules) 845 && Objects.equals(other.manualRule, manualRule) 846 && other.suppressedVisualEffects == suppressedVisualEffects 847 && other.areChannelsBypassingDnd == areChannelsBypassingDnd 848 && other.allowConversations == allowConversations 849 && other.allowConversationsFrom == allowConversationsFrom; 850 if (Flags.modesApi()) { 851 return eq 852 && Objects.equals(other.deletedRules, deletedRules) 853 && other.allowPriorityChannels == allowPriorityChannels; 854 } 855 return eq; 856 } 857 858 @Override hashCode()859 public int hashCode() { 860 // The policy fields that live on config are compared directly because the fields will 861 // contain data until MODES_UI is rolled out/cleaned up. 862 if (Flags.modesApi()) { 863 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, 864 allowRepeatCallers, allowMessages, 865 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, 866 user, automaticRules, manualRule, 867 suppressedVisualEffects, areChannelsBypassingDnd, allowConversations, 868 allowConversationsFrom, allowPriorityChannels); 869 } 870 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, 871 allowRepeatCallers, allowMessages, 872 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, 873 user, automaticRules, manualRule, 874 suppressedVisualEffects, areChannelsBypassingDnd, allowConversations, 875 allowConversationsFrom); 876 } 877 toDayList(int[] days)878 private static String toDayList(int[] days) { 879 if (days == null || days.length == 0) return ""; 880 final StringBuilder sb = new StringBuilder(); 881 for (int i = 0; i < days.length; i++) { 882 if (i > 0) sb.append('.'); 883 sb.append(days[i]); 884 } 885 return sb.toString(); 886 } 887 tryParseDayList(String dayList, String sep)888 private static int[] tryParseDayList(String dayList, String sep) { 889 if (dayList == null) return null; 890 final String[] tokens = dayList.split(sep); 891 if (tokens.length == 0) return null; 892 final int[] rt = new int[tokens.length]; 893 for (int i = 0; i < tokens.length; i++) { 894 final int day = tryParseInt(tokens[i], -1); 895 if (day == -1) return null; 896 rt[i] = day; 897 } 898 return rt; 899 } 900 tryParseInt(String value, int defValue)901 private static int tryParseInt(String value, int defValue) { 902 if (TextUtils.isEmpty(value)) return defValue; 903 try { 904 return Integer.parseInt(value); 905 } catch (NumberFormatException e) { 906 return defValue; 907 } 908 } 909 tryParseLong(String value, long defValue)910 private static long tryParseLong(String value, long defValue) { 911 if (TextUtils.isEmpty(value)) return defValue; 912 try { 913 return Long.parseLong(value); 914 } catch (NumberFormatException e) { 915 return defValue; 916 } 917 } 918 tryParseLong(String value, Long defValue)919 private static Long tryParseLong(String value, Long defValue) { 920 if (TextUtils.isEmpty(value)) return defValue; 921 try { 922 return Long.parseLong(value); 923 } catch (NumberFormatException e) { 924 return defValue; 925 } 926 } 927 getCurrentXmlVersion()928 public static int getCurrentXmlVersion() { 929 return Flags.modesApi() ? XML_VERSION_MODES_API : XML_VERSION; 930 } 931 readXml(TypedXmlPullParser parser)932 public static ZenModeConfig readXml(TypedXmlPullParser parser) 933 throws XmlPullParserException, IOException { 934 int type = parser.getEventType(); 935 if (type != XmlPullParser.START_TAG) return null; 936 String tag = parser.getName(); 937 if (!ZEN_TAG.equals(tag)) return null; 938 final ZenModeConfig rt = new ZenModeConfig(); 939 rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion()); 940 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user); 941 boolean readSuppressedEffects = false; 942 boolean readManualRule = false; 943 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 944 tag = parser.getName(); 945 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { 946 if (Flags.modesUi() && !readManualRule) { 947 // migrate from fields on config into manual rule 948 rt.manualRule.zenPolicy = rt.toZenPolicy(); 949 } 950 return rt; 951 } 952 if (type == XmlPullParser.START_TAG) { 953 if (ALLOW_TAG.equals(tag)) { 954 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, 955 DEFAULT_ALLOW_CALLS); 956 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS, 957 DEFAULT_ALLOW_REPEAT_CALLERS); 958 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, 959 DEFAULT_ALLOW_MESSAGES); 960 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS, 961 DEFAULT_ALLOW_REMINDERS); 962 rt.allowConversations = safeBoolean(parser, ALLOW_ATT_CONV, DEFAULT_ALLOW_CONV); 963 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS); 964 final int from = safeInt(parser, ALLOW_ATT_FROM, -1); 965 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1); 966 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1); 967 rt.allowConversationsFrom = safeInt(parser, ALLOW_ATT_CONV_FROM, 968 DEFAULT_ALLOW_CONV_FROM); 969 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) { 970 rt.allowCallsFrom = callsFrom; 971 rt.allowMessagesFrom = messagesFrom; 972 } else if (isValidSource(from)) { 973 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from)); 974 rt.allowCallsFrom = from; 975 rt.allowMessagesFrom = from; 976 } else { 977 rt.allowCallsFrom = DEFAULT_CALLS_SOURCE; 978 rt.allowMessagesFrom = DEFAULT_SOURCE; 979 } 980 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS); 981 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA, 982 DEFAULT_ALLOW_MEDIA); 983 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM); 984 if (Flags.modesApi()) { 985 rt.allowPriorityChannels = safeBoolean(parser, ALLOW_ATT_CHANNELS, 986 DEFAULT_ALLOW_PRIORITY_CHANNELS); 987 } 988 989 // migrate old suppressed visual effects fields, if they still exist in the xml 990 Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF); 991 Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON); 992 if (allowWhenScreenOff != null || allowWhenScreenOn != null) { 993 // If either setting exists, then reset the suppressed visual effects field 994 // to 0 (all allowed) so that only the relevant bits are disallowed by 995 // the migrated settings. 996 readSuppressedEffects = true; 997 rt.suppressedVisualEffects = 0; 998 } 999 if (allowWhenScreenOff != null) { 1000 if (!allowWhenScreenOff) { 1001 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS 1002 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT 1003 | SUPPRESSED_EFFECT_AMBIENT; 1004 } 1005 } 1006 if (allowWhenScreenOn != null) { 1007 if (!allowWhenScreenOn) { 1008 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK; 1009 } 1010 } 1011 if (readSuppressedEffects) { 1012 Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects); 1013 } 1014 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) { 1015 // only read from suppressed visual effects field if we haven't just migrated 1016 // the values from allowOn/allowOff, lest we wipe out those settings 1017 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS, 1018 DEFAULT_SUPPRESSED_VISUAL_EFFECTS); 1019 } else if (MANUAL_TAG.equals(tag)) { 1020 rt.manualRule = readRuleXml(parser); 1021 if (rt.manualRule != null) { 1022 readManualRule = true; 1023 } 1024 } else if (AUTOMATIC_TAG.equals(tag) 1025 || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) { 1026 final String id = parser.getAttributeValue(null, RULE_ATT_ID); 1027 final ZenRule automaticRule = readRuleXml(parser); 1028 if (id != null && automaticRule != null) { 1029 automaticRule.id = id; 1030 if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) { 1031 String deletedRuleKey = deletedRuleKey(automaticRule); 1032 if (deletedRuleKey != null) { 1033 rt.deletedRules.put(deletedRuleKey, automaticRule); 1034 } 1035 } else if (AUTOMATIC_TAG.equals(tag)) { 1036 rt.automaticRules.put(id, automaticRule); 1037 } 1038 } 1039 } else if (STATE_TAG.equals(tag)) { 1040 rt.areChannelsBypassingDnd = safeBoolean(parser, 1041 STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND); 1042 } 1043 } 1044 } 1045 throw new IllegalStateException("Failed to reach END_DOCUMENT"); 1046 } 1047 1048 /** Generates the map key used for a {@link ZenRule} in {@link #deletedRules}. */ 1049 @Nullable deletedRuleKey(ZenRule rule)1050 public static String deletedRuleKey(ZenRule rule) { 1051 if (rule.pkg != null && rule.conditionId != null) { 1052 return rule.pkg + "|" + rule.conditionId.toString(); 1053 } else { 1054 return null; 1055 } 1056 } 1057 1058 /** 1059 * Writes XML of current ZenModeConfig 1060 * @param out serializer 1061 * @param version uses the current XML version if version is null 1062 * @throws IOException 1063 */ 1064 writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)1065 public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup) 1066 throws IOException { 1067 int xmlVersion = getCurrentXmlVersion(); 1068 out.startTag(null, ZEN_TAG); 1069 out.attribute(null, ZEN_ATT_VERSION, version == null 1070 ? Integer.toString(xmlVersion) : Integer.toString(version)); 1071 out.attributeInt(null, ZEN_ATT_USER, user); 1072 out.startTag(null, ALLOW_TAG); 1073 // From MODES_UI these fields are only read if the flag has transitioned from off to on 1074 // However, we will continue to write these fields until the flag is cleaned up so it's 1075 // possible to turn the flag off without losing user data 1076 out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls); 1077 out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers); 1078 out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages); 1079 out.attributeBoolean(null, ALLOW_ATT_REMINDERS, allowReminders); 1080 out.attributeBoolean(null, ALLOW_ATT_EVENTS, allowEvents); 1081 out.attributeInt(null, ALLOW_ATT_CALLS_FROM, allowCallsFrom); 1082 out.attributeInt(null, ALLOW_ATT_MESSAGES_FROM, allowMessagesFrom); 1083 out.attributeBoolean(null, ALLOW_ATT_ALARMS, allowAlarms); 1084 out.attributeBoolean(null, ALLOW_ATT_MEDIA, allowMedia); 1085 out.attributeBoolean(null, ALLOW_ATT_SYSTEM, allowSystem); 1086 out.attributeBoolean(null, ALLOW_ATT_CONV, allowConversations); 1087 out.attributeInt(null, ALLOW_ATT_CONV_FROM, allowConversationsFrom); 1088 if (Flags.modesApi()) { 1089 out.attributeBoolean(null, ALLOW_ATT_CHANNELS, allowPriorityChannels); 1090 } 1091 out.endTag(null, ALLOW_TAG); 1092 1093 out.startTag(null, DISALLOW_TAG); 1094 out.attributeInt(null, DISALLOW_ATT_VISUAL_EFFECTS, suppressedVisualEffects); 1095 out.endTag(null, DISALLOW_TAG); 1096 1097 if (manualRule != null) { 1098 out.startTag(null, MANUAL_TAG); 1099 writeRuleXml(manualRule, out); 1100 out.endTag(null, MANUAL_TAG); 1101 } 1102 final int N = automaticRules.size(); 1103 for (int i = 0; i < N; i++) { 1104 final String id = automaticRules.keyAt(i); 1105 final ZenRule automaticRule = automaticRules.valueAt(i); 1106 out.startTag(null, AUTOMATIC_TAG); 1107 out.attribute(null, RULE_ATT_ID, id); 1108 writeRuleXml(automaticRule, out); 1109 out.endTag(null, AUTOMATIC_TAG); 1110 } 1111 if (Flags.modesApi() && !forBackup) { 1112 for (int i = 0; i < deletedRules.size(); i++) { 1113 final ZenRule deletedRule = deletedRules.valueAt(i); 1114 out.startTag(null, AUTOMATIC_DELETED_TAG); 1115 out.attribute(null, RULE_ATT_ID, deletedRule.id); 1116 writeRuleXml(deletedRule, out); 1117 out.endTag(null, AUTOMATIC_DELETED_TAG); 1118 } 1119 } 1120 1121 out.startTag(null, STATE_TAG); 1122 out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd); 1123 out.endTag(null, STATE_TAG); 1124 1125 out.endTag(null, ZEN_TAG); 1126 } 1127 readRuleXml(TypedXmlPullParser parser)1128 public static ZenRule readRuleXml(TypedXmlPullParser parser) { 1129 final ZenRule rt = new ZenRule(); 1130 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); 1131 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); 1132 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN); 1133 rt.zenMode = tryParseZenMode(zen, -1); 1134 if (rt.zenMode == -1) { 1135 Slog.w(TAG, "Bad zen mode in rule xml:" + zen); 1136 return null; 1137 } 1138 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); 1139 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); 1140 rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY); 1141 rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG); 1142 if (rt.pkg == null) { 1143 // backfill from component, if present. configActivity is not safe to backfill from 1144 rt.pkg = rt.component != null ? rt.component.getPackageName() : null; 1145 } 1146 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); 1147 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); 1148 rt.condition = readConditionXml(parser); 1149 1150 if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS 1151 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) { 1152 // all default rules and user created rules updated to zenMode important interruptions 1153 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name); 1154 rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; 1155 } 1156 rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); 1157 rt.zenPolicy = readZenPolicyXml(parser); 1158 if (Flags.modesApi()) { 1159 rt.zenDeviceEffects = readZenDeviceEffectsXml(parser); 1160 rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false); 1161 rt.iconResName = parser.getAttributeValue(null, RULE_ATT_ICON); 1162 rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC); 1163 rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN); 1164 rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0); 1165 rt.zenPolicyUserModifiedFields = safeInt(parser, POLICY_USER_MODIFIED_FIELDS, 0); 1166 rt.zenDeviceEffectsUserModifiedFields = safeInt(parser, 1167 DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0); 1168 Long deletionInstant = tryParseLong( 1169 parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null); 1170 if (deletionInstant != null) { 1171 rt.deletionInstant = Instant.ofEpochMilli(deletionInstant); 1172 } 1173 } 1174 return rt; 1175 } 1176 writeRuleXml(ZenRule rule, TypedXmlSerializer out)1177 public static void writeRuleXml(ZenRule rule, TypedXmlSerializer out) throws IOException { 1178 out.attributeBoolean(null, RULE_ATT_ENABLED, rule.enabled); 1179 if (rule.name != null) { 1180 out.attribute(null, RULE_ATT_NAME, rule.name); 1181 } 1182 out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode); 1183 if (rule.pkg != null) { 1184 out.attribute(null, RULE_ATT_PKG, rule.pkg); 1185 } 1186 if (rule.component != null) { 1187 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); 1188 } 1189 if (rule.configurationActivity != null) { 1190 out.attribute(null, RULE_ATT_CONFIG_ACTIVITY, 1191 rule.configurationActivity.flattenToString()); 1192 } 1193 if (rule.conditionId != null) { 1194 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); 1195 } 1196 out.attributeLong(null, RULE_ATT_CREATION_TIME, rule.creationTime); 1197 if (rule.enabler != null) { 1198 out.attribute(null, RULE_ATT_ENABLER, rule.enabler); 1199 } 1200 if (rule.condition != null) { 1201 writeConditionXml(rule.condition, out); 1202 } 1203 if (rule.zenPolicy != null) { 1204 writeZenPolicyXml(rule.zenPolicy, out); 1205 } 1206 if (Flags.modesApi() && rule.zenDeviceEffects != null) { 1207 writeZenDeviceEffectsXml(rule.zenDeviceEffects, out); 1208 } 1209 out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified); 1210 if (Flags.modesApi()) { 1211 out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation); 1212 if (rule.iconResName != null) { 1213 out.attribute(null, RULE_ATT_ICON, rule.iconResName); 1214 } 1215 if (rule.triggerDescription != null) { 1216 out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription); 1217 } 1218 out.attributeInt(null, RULE_ATT_TYPE, rule.type); 1219 out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields); 1220 out.attributeInt(null, POLICY_USER_MODIFIED_FIELDS, rule.zenPolicyUserModifiedFields); 1221 out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS, 1222 rule.zenDeviceEffectsUserModifiedFields); 1223 if (rule.deletionInstant != null) { 1224 out.attributeLong(null, RULE_ATT_DELETION_INSTANT, 1225 rule.deletionInstant.toEpochMilli()); 1226 } 1227 } 1228 } 1229 readConditionXml(TypedXmlPullParser parser)1230 public static Condition readConditionXml(TypedXmlPullParser parser) { 1231 final Uri id = safeUri(parser, CONDITION_ATT_ID); 1232 if (id == null) return null; 1233 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); 1234 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1); 1235 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2); 1236 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1); 1237 final int state = safeInt(parser, CONDITION_ATT_STATE, -1); 1238 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1); 1239 try { 1240 if (Flags.modesApi()) { 1241 final int source = safeInt(parser, CONDITION_ATT_SOURCE, Condition.SOURCE_UNKNOWN); 1242 return new Condition(id, summary, line1, line2, icon, state, source, flags); 1243 } else { 1244 return new Condition(id, summary, line1, line2, icon, state, flags); 1245 } 1246 } catch (IllegalArgumentException e) { 1247 Slog.w(TAG, "Unable to read condition xml", e); 1248 return null; 1249 } 1250 } 1251 writeConditionXml(Condition c, TypedXmlSerializer out)1252 public static void writeConditionXml(Condition c, TypedXmlSerializer out) throws IOException { 1253 out.attribute(null, CONDITION_ATT_ID, c.id.toString()); 1254 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary); 1255 out.attribute(null, CONDITION_ATT_LINE1, c.line1); 1256 out.attribute(null, CONDITION_ATT_LINE2, c.line2); 1257 out.attributeInt(null, CONDITION_ATT_ICON, c.icon); 1258 out.attributeInt(null, CONDITION_ATT_STATE, c.state); 1259 if (Flags.modesApi()) { 1260 out.attributeInt(null, CONDITION_ATT_SOURCE, c.source); 1261 } 1262 out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags); 1263 } 1264 1265 /** 1266 * Read the zen policy from xml 1267 * Returns null if no zen policy exists 1268 */ readZenPolicyXml(TypedXmlPullParser parser)1269 public static ZenPolicy readZenPolicyXml(TypedXmlPullParser parser) { 1270 boolean policySet = false; 1271 1272 ZenPolicy.Builder builder = new ZenPolicy.Builder(); 1273 final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET); 1274 final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET); 1275 final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET); 1276 final int conversations = safeInt(parser, ALLOW_ATT_CONV_FROM, 1277 ZenPolicy.CONVERSATION_SENDERS_UNSET); 1278 final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET); 1279 final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET); 1280 final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET); 1281 final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET); 1282 final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET); 1283 if (Flags.modesApi()) { 1284 final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET); 1285 if (channels != ZenPolicy.STATE_UNSET) { 1286 builder.allowPriorityChannels(channels == STATE_ALLOW); 1287 policySet = true; 1288 } 1289 } 1290 1291 if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) { 1292 builder.allowCalls(calls); 1293 policySet = true; 1294 } 1295 if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) { 1296 builder.allowMessages(messages); 1297 policySet = true; 1298 } 1299 if (repeatCallers != ZenPolicy.STATE_UNSET) { 1300 builder.allowRepeatCallers(repeatCallers == STATE_ALLOW); 1301 policySet = true; 1302 } 1303 if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) { 1304 builder.allowConversations(conversations); 1305 policySet = true; 1306 } 1307 if (alarms != ZenPolicy.STATE_UNSET) { 1308 builder.allowAlarms(alarms == STATE_ALLOW); 1309 policySet = true; 1310 } 1311 if (media != ZenPolicy.STATE_UNSET) { 1312 builder.allowMedia(media == STATE_ALLOW); 1313 policySet = true; 1314 } 1315 if (system != ZenPolicy.STATE_UNSET) { 1316 builder.allowSystem(system == STATE_ALLOW); 1317 policySet = true; 1318 } 1319 if (events != ZenPolicy.STATE_UNSET) { 1320 builder.allowEvents(events == STATE_ALLOW); 1321 policySet = true; 1322 } 1323 if (reminders != ZenPolicy.STATE_UNSET) { 1324 builder.allowReminders(reminders == STATE_ALLOW); 1325 policySet = true; 1326 } 1327 1328 final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT, 1329 ZenPolicy.STATE_UNSET); 1330 final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET); 1331 final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET); 1332 final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET); 1333 final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET); 1334 final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET); 1335 final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST, 1336 ZenPolicy.STATE_UNSET); 1337 1338 if (fullScreenIntent != ZenPolicy.STATE_UNSET) { 1339 builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW); 1340 policySet = true; 1341 } 1342 if (lights != ZenPolicy.STATE_UNSET) { 1343 builder.showLights(lights == STATE_ALLOW); 1344 policySet = true; 1345 } 1346 if (peek != ZenPolicy.STATE_UNSET) { 1347 builder.showPeeking(peek == STATE_ALLOW); 1348 policySet = true; 1349 } 1350 if (statusBar != ZenPolicy.STATE_UNSET) { 1351 builder.showStatusBarIcons(statusBar == STATE_ALLOW); 1352 policySet = true; 1353 } 1354 if (badges != ZenPolicy.STATE_UNSET) { 1355 builder.showBadges(badges == STATE_ALLOW); 1356 policySet = true; 1357 } 1358 if (ambient != ZenPolicy.STATE_UNSET) { 1359 builder.showInAmbientDisplay(ambient == STATE_ALLOW); 1360 policySet = true; 1361 } 1362 if (notificationList != ZenPolicy.STATE_UNSET) { 1363 builder.showInNotificationList(notificationList == STATE_ALLOW); 1364 policySet = true; 1365 } 1366 1367 if (policySet) { 1368 return builder.build(); 1369 } 1370 return null; 1371 } 1372 1373 /** 1374 * Writes zen policy to xml 1375 */ writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out)1376 public static void writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out) 1377 throws IOException { 1378 writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out); 1379 writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out); 1380 writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(), 1381 out); 1382 writeZenPolicyState(ALLOW_ATT_CONV_FROM, policy.getPriorityConversationSenders(), out); 1383 writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out); 1384 writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out); 1385 writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out); 1386 writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out); 1387 writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out); 1388 1389 writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(), 1390 out); 1391 writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out); 1392 writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out); 1393 writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out); 1394 writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out); 1395 writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out); 1396 writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(), 1397 out); 1398 1399 if (Flags.modesApi()) { 1400 writeZenPolicyState(ALLOW_ATT_CHANNELS, policy.getPriorityChannelsAllowed(), out); 1401 } 1402 } 1403 writeZenPolicyState(String attr, int val, TypedXmlSerializer out)1404 private static void writeZenPolicyState(String attr, int val, TypedXmlSerializer out) 1405 throws IOException { 1406 if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM) 1407 || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) { 1408 if (val != ZenPolicy.PEOPLE_TYPE_UNSET) { 1409 out.attributeInt(null, attr, val); 1410 } 1411 } else if (Objects.equals(attr, ALLOW_ATT_CONV_FROM)) { 1412 if (val != ZenPolicy.CONVERSATION_SENDERS_UNSET) { 1413 out.attributeInt(null, attr, val); 1414 } 1415 } else if (Flags.modesApi() && Objects.equals(attr, ALLOW_ATT_CHANNELS)) { 1416 if (val != ZenPolicy.STATE_UNSET) { 1417 out.attributeInt(null, attr, val); 1418 } 1419 } else { 1420 if (val != ZenPolicy.STATE_UNSET) { 1421 out.attributeInt(null, attr, val); 1422 } 1423 } 1424 } 1425 1426 @FlaggedApi(Flags.FLAG_MODES_API) 1427 @Nullable readZenDeviceEffectsXml(TypedXmlPullParser parser)1428 private static ZenDeviceEffects readZenDeviceEffectsXml(TypedXmlPullParser parser) { 1429 ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder() 1430 .setShouldDisplayGrayscale( 1431 safeBoolean(parser, DEVICE_EFFECT_DISPLAY_GRAYSCALE, false)) 1432 .setShouldSuppressAmbientDisplay( 1433 safeBoolean(parser, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, false)) 1434 .setShouldDimWallpaper(safeBoolean(parser, DEVICE_EFFECT_DIM_WALLPAPER, false)) 1435 .setShouldUseNightMode(safeBoolean(parser, DEVICE_EFFECT_USE_NIGHT_MODE, false)) 1436 .setShouldDisableAutoBrightness( 1437 safeBoolean(parser, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, false)) 1438 .setShouldDisableTapToWake( 1439 safeBoolean(parser, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, false)) 1440 .setShouldDisableTiltToWake( 1441 safeBoolean(parser, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, false)) 1442 .setShouldDisableTouch(safeBoolean(parser, DEVICE_EFFECT_DISABLE_TOUCH, false)) 1443 .setShouldMinimizeRadioUsage( 1444 safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false)) 1445 .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false)) 1446 .setExtraEffects(safeStringSet(parser, DEVICE_EFFECT_EXTRAS)) 1447 .build(); 1448 1449 return deviceEffects.hasEffects() ? deviceEffects : null; 1450 } 1451 1452 @FlaggedApi(Flags.FLAG_MODES_API) writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects, TypedXmlSerializer out)1453 private static void writeZenDeviceEffectsXml(ZenDeviceEffects deviceEffects, 1454 TypedXmlSerializer out) throws IOException { 1455 writeBooleanIfTrue(out, DEVICE_EFFECT_DISPLAY_GRAYSCALE, 1456 deviceEffects.shouldDisplayGrayscale()); 1457 writeBooleanIfTrue(out, DEVICE_EFFECT_SUPPRESS_AMBIENT_DISPLAY, 1458 deviceEffects.shouldSuppressAmbientDisplay()); 1459 writeBooleanIfTrue(out, DEVICE_EFFECT_DIM_WALLPAPER, deviceEffects.shouldDimWallpaper()); 1460 writeBooleanIfTrue(out, DEVICE_EFFECT_USE_NIGHT_MODE, deviceEffects.shouldUseNightMode()); 1461 writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_AUTO_BRIGHTNESS, 1462 deviceEffects.shouldDisableAutoBrightness()); 1463 writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TAP_TO_WAKE, 1464 deviceEffects.shouldDisableTapToWake()); 1465 writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TILT_TO_WAKE, 1466 deviceEffects.shouldDisableTiltToWake()); 1467 writeBooleanIfTrue(out, DEVICE_EFFECT_DISABLE_TOUCH, deviceEffects.shouldDisableTouch()); 1468 writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, 1469 deviceEffects.shouldMinimizeRadioUsage()); 1470 writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze()); 1471 writeStringSet(out, DEVICE_EFFECT_EXTRAS, deviceEffects.getExtraEffects()); 1472 } 1473 writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)1474 private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value) 1475 throws IOException { 1476 if (value) { 1477 out.attributeBoolean(null, att, true); 1478 } 1479 } 1480 writeStringSet(TypedXmlSerializer out, String att, Set<String> values)1481 private static void writeStringSet(TypedXmlSerializer out, String att, Set<String> values) 1482 throws IOException { 1483 if (values.isEmpty()) { 1484 return; 1485 } 1486 // We escape each item by replacing "\" by "\\" and "," by "\,". Then we concatenate with 1487 // "," as separator. Reading performs the same operations in the opposite order. 1488 List<String> escapedItems = new ArrayList<>(); 1489 for (String item : values) { 1490 escapedItems.add( 1491 item 1492 .replace(ITEM_SEPARATOR_ESCAPE, 1493 ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR_ESCAPE) 1494 .replace(ITEM_SEPARATOR, 1495 ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR)); 1496 } 1497 String serialized = String.join(ITEM_SEPARATOR, escapedItems); 1498 out.attribute(null, att, serialized); 1499 } 1500 isValidHour(int val)1501 public static boolean isValidHour(int val) { 1502 return val >= 0 && val < 24; 1503 } 1504 isValidMinute(int val)1505 public static boolean isValidMinute(int val) { 1506 return val >= 0 && val < 60; 1507 } 1508 isValidSource(int source)1509 private static boolean isValidSource(int source) { 1510 return source >= SOURCE_ANYONE && source <= MAX_SOURCE; 1511 } 1512 unsafeBoolean(TypedXmlPullParser parser, String att)1513 private static Boolean unsafeBoolean(TypedXmlPullParser parser, String att) { 1514 try { 1515 return parser.getAttributeBoolean(null, att); 1516 } catch (Exception e) { 1517 return null; 1518 } 1519 } 1520 safeBoolean(TypedXmlPullParser parser, String att, boolean defValue)1521 private static boolean safeBoolean(TypedXmlPullParser parser, String att, boolean defValue) { 1522 return parser.getAttributeBoolean(null, att, defValue); 1523 } 1524 safeBoolean(String val, boolean defValue)1525 private static boolean safeBoolean(String val, boolean defValue) { 1526 if (TextUtils.isEmpty(val)) return defValue; 1527 return Boolean.parseBoolean(val); 1528 } 1529 safeInt(TypedXmlPullParser parser, String att, int defValue)1530 private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { 1531 return parser.getAttributeInt(null, att, defValue); 1532 } 1533 safeComponentName(TypedXmlPullParser parser, String att)1534 private static ComponentName safeComponentName(TypedXmlPullParser parser, String att) { 1535 final String val = parser.getAttributeValue(null, att); 1536 if (TextUtils.isEmpty(val)) return null; 1537 return ComponentName.unflattenFromString(val); 1538 } 1539 safeUri(TypedXmlPullParser parser, String att)1540 private static Uri safeUri(TypedXmlPullParser parser, String att) { 1541 final String val = parser.getAttributeValue(null, att); 1542 if (val == null) return null; 1543 return Uri.parse(val); 1544 } 1545 safeLong(TypedXmlPullParser parser, String att, long defValue)1546 private static long safeLong(TypedXmlPullParser parser, String att, long defValue) { 1547 final String val = parser.getAttributeValue(null, att); 1548 return tryParseLong(val, defValue); 1549 } 1550 1551 @NonNull safeStringSet(TypedXmlPullParser parser, String att)1552 private static Set<String> safeStringSet(TypedXmlPullParser parser, String att) { 1553 Set<String> values = new HashSet<>(); 1554 1555 String serialized = parser.getAttributeValue(null, att); 1556 if (!TextUtils.isEmpty(serialized)) { 1557 // We split on every "," that is *not preceded* by the escape character "\". 1558 // Then we reverse the escaping done on each individual item. 1559 String[] escapedItems = ITEM_SPLITTER_REGEX.split(serialized); 1560 for (String escapedItem : escapedItems) { 1561 values.add(escapedItem 1562 .replace(ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR_ESCAPE, 1563 ITEM_SEPARATOR_ESCAPE) 1564 .replace(ITEM_SEPARATOR_ESCAPE + ITEM_SEPARATOR, 1565 ITEM_SEPARATOR)); 1566 } 1567 } 1568 return values; 1569 } 1570 1571 @Override describeContents()1572 public int describeContents() { 1573 return 0; 1574 } 1575 copy()1576 public ZenModeConfig copy() { 1577 final Parcel parcel = Parcel.obtain(); 1578 try { 1579 writeToParcel(parcel, 0); 1580 parcel.setDataPosition(0); 1581 return new ZenModeConfig(parcel); 1582 } finally { 1583 parcel.recycle(); 1584 } 1585 } 1586 1587 public static final @android.annotation.NonNull Parcelable.Creator<ZenModeConfig> CREATOR 1588 = new Parcelable.Creator<ZenModeConfig>() { 1589 @Override 1590 public ZenModeConfig createFromParcel(Parcel source) { 1591 return new ZenModeConfig(source); 1592 } 1593 1594 @Override 1595 public ZenModeConfig[] newArray(int size) { 1596 return new ZenModeConfig[size]; 1597 } 1598 }; 1599 getZenPolicy()1600 public ZenPolicy getZenPolicy() { 1601 return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy(); 1602 } 1603 1604 /** 1605 * Converts a ZenModeConfig to a ZenPolicy 1606 */ 1607 @VisibleForTesting toZenPolicy()1608 ZenPolicy toZenPolicy() { 1609 ZenPolicy.Builder builder = new ZenPolicy.Builder() 1610 .allowCalls(allowCalls 1611 ? prioritySendersToPeopleType(allowCallsFrom) 1612 : ZenPolicy.PEOPLE_TYPE_NONE) 1613 .allowRepeatCallers(allowRepeatCallers) 1614 .allowMessages(allowMessages 1615 ? prioritySendersToPeopleType(allowMessagesFrom) 1616 : ZenPolicy.PEOPLE_TYPE_NONE) 1617 .allowReminders(allowReminders) 1618 .allowEvents(allowEvents) 1619 .allowAlarms(allowAlarms) 1620 .allowMedia(allowMedia) 1621 .allowSystem(allowSystem) 1622 .allowConversations(allowConversations ? allowConversationsFrom 1623 : ZenPolicy.CONVERSATION_SENDERS_NONE); 1624 if (suppressedVisualEffects == 0) { 1625 builder.showAllVisualEffects(); 1626 } else { 1627 // configs don't have an unset state: wither true or false. 1628 builder.showFullScreenIntent( 1629 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0); 1630 builder.showLights( 1631 (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0); 1632 builder.showPeeking( 1633 (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0); 1634 builder.showStatusBarIcons( 1635 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0); 1636 builder.showBadges( 1637 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0); 1638 builder.showInAmbientDisplay( 1639 (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0); 1640 builder.showInNotificationList( 1641 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0); 1642 } 1643 1644 if (Flags.modesApi()) { 1645 builder.allowPriorityChannels(allowPriorityChannels); 1646 } 1647 return builder.build(); 1648 } 1649 1650 /** 1651 * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its 1652 * defaults for all unset values in zenPolicy 1653 */ toNotificationPolicy(ZenPolicy zenPolicy)1654 public Policy toNotificationPolicy(ZenPolicy zenPolicy) { 1655 NotificationManager.Policy defaultPolicy = toNotificationPolicy(); 1656 int priorityCategories = 0; 1657 int suppressedVisualEffects = 0; 1658 int callSenders = defaultPolicy.priorityCallSenders; 1659 int messageSenders = defaultPolicy.priorityMessageSenders; 1660 int conversationSenders = defaultPolicy.priorityConversationSenders; 1661 1662 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS, 1663 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) { 1664 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; 1665 } 1666 1667 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, 1668 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) { 1669 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; 1670 } 1671 1672 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, 1673 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { 1674 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; 1675 messageSenders = peopleTypeToPrioritySenders( 1676 zenPolicy.getPriorityMessageSenders(), messageSenders); 1677 } 1678 1679 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS, 1680 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) { 1681 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; 1682 conversationSenders = zenPolicyConversationSendersToNotificationPolicy( 1683 zenPolicy.getPriorityConversationSenders(), conversationSenders); 1684 } else { 1685 conversationSenders = CONVERSATION_SENDERS_NONE; 1686 } 1687 1688 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, 1689 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { 1690 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; 1691 callSenders = peopleTypeToPrioritySenders( 1692 zenPolicy.getPriorityCallSenders(), callSenders); 1693 } 1694 1695 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, 1696 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 1697 defaultPolicy))) { 1698 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; 1699 } 1700 1701 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, 1702 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) { 1703 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; 1704 } 1705 1706 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, 1707 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) { 1708 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; 1709 } 1710 1711 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, 1712 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) { 1713 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; 1714 } 1715 1716 boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed( 1717 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, 1718 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, 1719 defaultPolicy)); 1720 1721 boolean suppressLights = !zenPolicy.isVisualEffectAllowed( 1722 ZenPolicy.VISUAL_EFFECT_LIGHTS, 1723 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS, 1724 defaultPolicy)); 1725 1726 boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed( 1727 ZenPolicy.VISUAL_EFFECT_AMBIENT, 1728 isVisualEffectAllowed(SUPPRESSED_EFFECT_AMBIENT, 1729 defaultPolicy)); 1730 1731 if (suppressFullScreenIntent && suppressLights && suppressAmbient) { 1732 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 1733 } 1734 1735 if (suppressFullScreenIntent) { 1736 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 1737 } 1738 1739 if (suppressLights) { 1740 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; 1741 } 1742 1743 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, 1744 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK, 1745 defaultPolicy))) { 1746 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; 1747 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; 1748 } 1749 1750 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, 1751 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR, 1752 defaultPolicy))) { 1753 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; 1754 } 1755 1756 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, 1757 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE, 1758 defaultPolicy))) { 1759 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; 1760 } 1761 1762 if (suppressAmbient) { 1763 suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; 1764 } 1765 1766 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, 1767 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST, 1768 defaultPolicy))) { 1769 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 1770 } 1771 1772 int state = defaultPolicy.state; 1773 if (Flags.modesApi()) { 1774 state = Policy.policyState(defaultPolicy.hasPriorityChannels(), 1775 ZenPolicy.stateToBoolean(zenPolicy.getPriorityChannelsAllowed(), 1776 DEFAULT_ALLOW_PRIORITY_CHANNELS)); 1777 } 1778 1779 return new NotificationManager.Policy(priorityCategories, callSenders, 1780 messageSenders, suppressedVisualEffects, state, conversationSenders); 1781 } 1782 isPriorityCategoryEnabled(int categoryType, Policy policy)1783 private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) { 1784 return (policy.priorityCategories & categoryType) != 0; 1785 } 1786 isVisualEffectAllowed(int visualEffect, Policy policy)1787 private boolean isVisualEffectAllowed(int visualEffect, Policy policy) { 1788 return (policy.suppressedVisualEffects & visualEffect) == 0; 1789 } 1790 isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect)1791 private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) { 1792 return (suppressedVisualEffects & visualEffect) == 0; 1793 } 1794 toNotificationPolicy()1795 public Policy toNotificationPolicy() { 1796 int priorityCategories = 0; 1797 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; 1798 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS; 1799 int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT; 1800 int state = 0; 1801 int suppressedVisualEffects = 0; 1802 1803 if (Flags.modesUi()) { 1804 if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) { 1805 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; 1806 } 1807 if (manualRule.zenPolicy.isCategoryAllowed( 1808 ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) { 1809 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; 1810 } 1811 if (manualRule.zenPolicy.isCategoryAllowed( 1812 ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) { 1813 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; 1814 } 1815 if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) { 1816 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; 1817 } 1818 if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) { 1819 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; 1820 } 1821 if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) { 1822 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; 1823 } 1824 1825 if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) { 1826 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; 1827 } 1828 priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( 1829 manualRule.zenPolicy.getPriorityConversationSenders(), 1830 CONVERSATION_SENDERS_NONE); 1831 if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) { 1832 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; 1833 } 1834 priorityCallSenders = peopleTypeToPrioritySenders( 1835 manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); 1836 if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) { 1837 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; 1838 } 1839 priorityMessageSenders = peopleTypeToPrioritySenders( 1840 manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE); 1841 1842 state = Policy.policyState(areChannelsBypassingDnd, 1843 manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW); 1844 1845 boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed( 1846 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, 1847 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1848 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT)); 1849 1850 boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed( 1851 ZenPolicy.VISUAL_EFFECT_LIGHTS, 1852 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1853 ZenPolicy.VISUAL_EFFECT_LIGHTS)); 1854 1855 boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed( 1856 ZenPolicy.VISUAL_EFFECT_AMBIENT, 1857 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1858 ZenPolicy.VISUAL_EFFECT_AMBIENT)); 1859 1860 if (suppressFullScreenIntent && suppressLights && suppressAmbient) { 1861 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 1862 } 1863 1864 if (suppressFullScreenIntent) { 1865 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 1866 } 1867 1868 if (suppressLights) { 1869 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; 1870 } 1871 1872 if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, 1873 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1874 ZenPolicy.VISUAL_EFFECT_PEEK))) { 1875 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; 1876 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; 1877 } 1878 1879 if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, 1880 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1881 ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) { 1882 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; 1883 } 1884 1885 if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, 1886 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1887 ZenPolicy.VISUAL_EFFECT_BADGE))) { 1888 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; 1889 } 1890 1891 if (suppressAmbient) { 1892 suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; 1893 } 1894 1895 if (!manualRule.zenPolicy.isVisualEffectAllowed( 1896 ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, 1897 isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, 1898 ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) { 1899 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 1900 } 1901 } else { 1902 if (isAllowConversations()) { 1903 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; 1904 } 1905 if (isAllowCalls()) { 1906 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; 1907 } 1908 if (isAllowMessages()) { 1909 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; 1910 } 1911 if (isAllowEvents()) { 1912 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; 1913 } 1914 if (isAllowReminders()) { 1915 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; 1916 } 1917 if (isAllowRepeatCallers()) { 1918 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; 1919 } 1920 if (isAllowAlarms()) { 1921 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; 1922 } 1923 if (isAllowMedia()) { 1924 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; 1925 } 1926 if (isAllowSystem()) { 1927 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; 1928 } 1929 priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders); 1930 priorityMessageSenders = sourceToPrioritySenders( 1931 getAllowMessagesFrom(), priorityMessageSenders); 1932 priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( 1933 getAllowConversationsFrom(), priorityConversationSenders); 1934 1935 state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; 1936 if (Flags.modesApi()) { 1937 state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels); 1938 } 1939 suppressedVisualEffects = getSuppressedVisualEffects(); 1940 } 1941 1942 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders, 1943 suppressedVisualEffects, state, priorityConversationSenders); 1944 } 1945 1946 /** 1947 * Creates scheduleCalendar from a condition id 1948 * @param conditionId 1949 * @return ScheduleCalendar with info populated with conditionId 1950 */ toScheduleCalendar(Uri conditionId)1951 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) { 1952 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId); 1953 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null; 1954 final ScheduleCalendar sc = new ScheduleCalendar(); 1955 sc.setSchedule(schedule); 1956 sc.setTimeZone(TimeZone.getDefault()); 1957 return sc; 1958 } 1959 sourceToPrioritySenders(int source, int def)1960 private static int sourceToPrioritySenders(int source, int def) { 1961 switch (source) { 1962 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY; 1963 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS; 1964 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED; 1965 default: return def; 1966 } 1967 } 1968 normalizePrioritySenders(int prioritySenders, int def)1969 private static int normalizePrioritySenders(int prioritySenders, int def) { 1970 if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS 1971 || prioritySenders == Policy.PRIORITY_SENDERS_STARRED 1972 || prioritySenders == Policy.PRIORITY_SENDERS_ANY)) { 1973 return def; 1974 } 1975 return prioritySenders; 1976 } 1977 normalizeConversationSenders(boolean allowed, int senders, int def)1978 private static int normalizeConversationSenders(boolean allowed, int senders, int def) { 1979 if (!allowed) { 1980 return CONVERSATION_SENDERS_NONE; 1981 } 1982 if (!(senders == CONVERSATION_SENDERS_ANYONE 1983 || senders == CONVERSATION_SENDERS_IMPORTANT 1984 || senders == CONVERSATION_SENDERS_NONE)) { 1985 return def; 1986 } 1987 return senders; 1988 } 1989 applyNotificationPolicy(Policy policy)1990 public void applyNotificationPolicy(Policy policy) { 1991 if (policy == null) return; 1992 if (Flags.modesUi()) { 1993 manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy); 1994 } else { 1995 setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0); 1996 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; 1997 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; 1998 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; 1999 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; 2000 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; 2001 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; 2002 allowRepeatCallers = 2003 (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) 2004 != 0; 2005 allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); 2006 allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, 2007 allowMessagesFrom); 2008 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { 2009 suppressedVisualEffects = policy.suppressedVisualEffects; 2010 } 2011 allowConversations = (policy.priorityCategories 2012 & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; 2013 allowConversationsFrom = normalizeConversationSenders(allowConversations, 2014 policy.priorityConversationSenders, 2015 allowConversationsFrom); 2016 if (policy.state != Policy.STATE_UNSET) { 2017 if (Flags.modesApi()) { 2018 setAllowPriorityChannels(policy.allowPriorityChannels()); 2019 } 2020 } 2021 } 2022 if (policy.state != Policy.STATE_UNSET) { 2023 areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; 2024 } 2025 } 2026 toTimeCondition(Context context, int minutesFromNow, int userHandle)2027 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { 2028 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/); 2029 } 2030 toTimeCondition(Context context, int minutesFromNow, int userHandle, boolean shortVersion)2031 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle, 2032 boolean shortVersion) { 2033 final long now = System.currentTimeMillis(); 2034 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; 2035 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion); 2036 } 2037 toTimeCondition(Context context, long time, int minutes, int userHandle, boolean shortVersion)2038 public static Condition toTimeCondition(Context context, long time, int minutes, 2039 int userHandle, boolean shortVersion) { 2040 final int num; 2041 String summary, line1, line2; 2042 final CharSequence formattedTime = 2043 getFormattedTime(context, time, isToday(time), userHandle); 2044 final Resources res = context.getResources(); 2045 final Map<String, Object> arguments = new HashMap<>(); 2046 if (minutes < 60) { 2047 // display as minutes 2048 num = minutes; 2049 int summaryResId = shortVersion ? R.string.zen_mode_duration_minutes_summary_short 2050 : R.string.zen_mode_duration_minutes_summary; 2051 arguments.put("count", num); 2052 arguments.put("formattedTime", formattedTime); 2053 summary = PluralsMessageFormatter.format(res, arguments, summaryResId); 2054 int line1ResId = shortVersion ? R.string.zen_mode_duration_minutes_short 2055 : R.string.zen_mode_duration_minutes; 2056 line1 = PluralsMessageFormatter.format(res, arguments, line1ResId); 2057 line2 = res.getString(R.string.zen_mode_until, formattedTime); 2058 } else if (minutes < DAY_MINUTES) { 2059 // display as hours 2060 num = Math.round(minutes / 60f); 2061 int summaryResId = shortVersion ? R.string.zen_mode_duration_hours_summary_short 2062 : R.string.zen_mode_duration_hours_summary; 2063 arguments.put("count", num); 2064 arguments.put("formattedTime", formattedTime); 2065 summary = PluralsMessageFormatter.format(res, arguments, summaryResId); 2066 int line1ResId = shortVersion ? R.string.zen_mode_duration_hours_short 2067 : R.string.zen_mode_duration_hours; 2068 line1 = PluralsMessageFormatter.format(res, arguments, line1ResId); 2069 line2 = res.getString(R.string.zen_mode_until, formattedTime); 2070 } else { 2071 // display as day/time 2072 summary = line1 = line2 = res.getString(R.string.zen_mode_until_next_day, 2073 formattedTime); 2074 } 2075 final Uri id = toCountdownConditionId(time, false); 2076 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE, 2077 Condition.FLAG_RELEVANT_NOW); 2078 } 2079 2080 /** 2081 * Converts countdown to alarm parameters into a condition with user facing summary 2082 */ toNextAlarmCondition(Context context, long alarm, int userHandle)2083 public static Condition toNextAlarmCondition(Context context, long alarm, 2084 int userHandle) { 2085 boolean isSameDay = isToday(alarm); 2086 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle); 2087 final Resources res = context.getResources(); 2088 final String line1 = res.getString(R.string.zen_mode_until, formattedTime); 2089 final Uri id = toCountdownConditionId(alarm, true); 2090 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE, 2091 Condition.FLAG_RELEVANT_NOW); 2092 } 2093 2094 /** 2095 * Creates readable time from time in milliseconds 2096 */ getFormattedTime(Context context, long time, boolean isSameDay, int userHandle)2097 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay, 2098 int userHandle) { 2099 String skeleton = (!isSameDay ? "EEE " : "") 2100 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"); 2101 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 2102 return DateFormat.format(pattern, time); 2103 } 2104 2105 /** 2106 * Determines whether a time in milliseconds is today or not 2107 */ isToday(long time)2108 public static boolean isToday(long time) { 2109 GregorianCalendar now = new GregorianCalendar(); 2110 GregorianCalendar endTime = new GregorianCalendar(); 2111 endTime.setTimeInMillis(time); 2112 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR) 2113 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH) 2114 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) { 2115 return true; 2116 } 2117 return false; 2118 } 2119 2120 // ==== Built-in system conditions ==== 2121 2122 public static final String SYSTEM_AUTHORITY = "android"; 2123 2124 // ==== Built-in system condition: countdown ==== 2125 2126 public static final String COUNTDOWN_PATH = "countdown"; 2127 2128 public static final String IS_ALARM_PATH = "alarm"; 2129 2130 /** 2131 * Converts countdown condition parameters into a condition id. 2132 */ toCountdownConditionId(long time, boolean alarm)2133 public static Uri toCountdownConditionId(long time, boolean alarm) { 2134 return new Uri.Builder().scheme(Condition.SCHEME) 2135 .authority(SYSTEM_AUTHORITY) 2136 .appendPath(COUNTDOWN_PATH) 2137 .appendPath(Long.toString(time)) 2138 .appendPath(IS_ALARM_PATH) 2139 .appendPath(Boolean.toString(alarm)) 2140 .build(); 2141 } 2142 tryParseCountdownConditionId(Uri conditionId)2143 public static long tryParseCountdownConditionId(Uri conditionId) { 2144 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0; 2145 if (conditionId.getPathSegments().size() < 2 2146 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0; 2147 try { 2148 return Long.parseLong(conditionId.getPathSegments().get(1)); 2149 } catch (RuntimeException e) { 2150 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e); 2151 return 0; 2152 } 2153 } 2154 2155 /** 2156 * Returns whether this condition is a countdown condition. 2157 */ isValidCountdownConditionId(Uri conditionId)2158 public static boolean isValidCountdownConditionId(Uri conditionId) { 2159 return tryParseCountdownConditionId(conditionId) != 0; 2160 } 2161 2162 /** 2163 * Returns whether this condition is a countdown to an alarm. 2164 */ isValidCountdownToAlarmConditionId(Uri conditionId)2165 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) { 2166 if (tryParseCountdownConditionId(conditionId) != 0) { 2167 if (conditionId.getPathSegments().size() < 4 2168 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) { 2169 return false; 2170 } 2171 try { 2172 return Boolean.parseBoolean(conditionId.getPathSegments().get(3)); 2173 } catch (RuntimeException e) { 2174 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e); 2175 return false; 2176 } 2177 } 2178 return false; 2179 } 2180 2181 // ==== Built-in system condition: schedule ==== 2182 2183 public static final String SCHEDULE_PATH = "schedule"; 2184 toScheduleConditionId(ScheduleInfo schedule)2185 public static Uri toScheduleConditionId(ScheduleInfo schedule) { 2186 return new Uri.Builder().scheme(Condition.SCHEME) 2187 .authority(SYSTEM_AUTHORITY) 2188 .appendPath(SCHEDULE_PATH) 2189 .appendQueryParameter("days", toDayList(schedule.days)) 2190 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) 2191 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) 2192 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm)) 2193 .build(); 2194 } 2195 isValidScheduleConditionId(Uri conditionId)2196 public static boolean isValidScheduleConditionId(Uri conditionId) { 2197 ScheduleInfo info; 2198 try { 2199 info = tryParseScheduleConditionId(conditionId); 2200 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { 2201 return false; 2202 } 2203 2204 if (info == null || info.days == null || info.days.length == 0) { 2205 return false; 2206 } 2207 return true; 2208 } 2209 2210 /** 2211 * Returns whether the conditionId is a valid ScheduleCondition. 2212 * If allowNever is true, this will return true even if the ScheduleCondition never occurs. 2213 */ isValidScheduleConditionId(Uri conditionId, boolean allowNever)2214 public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) { 2215 ScheduleInfo info; 2216 try { 2217 info = tryParseScheduleConditionId(conditionId); 2218 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { 2219 return false; 2220 } 2221 2222 if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) { 2223 return false; 2224 } 2225 return true; 2226 } 2227 2228 /** 2229 * Returns the {@link ScheduleInfo} encoded in the condition id, or {@code null} if it could not 2230 * be decoded. 2231 */ 2232 @UnsupportedAppUsage 2233 @Nullable tryParseScheduleConditionId(Uri conditionId)2234 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { 2235 final boolean isSchedule = conditionId != null 2236 && Condition.SCHEME.equals(conditionId.getScheme()) 2237 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority()) 2238 && conditionId.getPathSegments().size() == 1 2239 && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0)); 2240 if (!isSchedule) return null; 2241 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); 2242 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); 2243 if (start == null || end == null) return null; 2244 final ScheduleInfo rt = new ScheduleInfo(); 2245 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); 2246 rt.startHour = start[0]; 2247 rt.startMinute = start[1]; 2248 rt.endHour = end[0]; 2249 rt.endMinute = end[1]; 2250 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false); 2251 return rt; 2252 } 2253 getScheduleConditionProvider()2254 public static ComponentName getScheduleConditionProvider() { 2255 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider"); 2256 } 2257 2258 public static class ScheduleInfo { 2259 @UnsupportedAppUsage 2260 public int[] days; 2261 @UnsupportedAppUsage 2262 public int startHour; 2263 @UnsupportedAppUsage 2264 public int startMinute; 2265 @UnsupportedAppUsage 2266 public int endHour; 2267 @UnsupportedAppUsage 2268 public int endMinute; 2269 public boolean exitAtAlarm; 2270 public long nextAlarm; 2271 2272 @Override hashCode()2273 public int hashCode() { 2274 return 0; 2275 } 2276 2277 @Override equals(@ullable Object o)2278 public boolean equals(@Nullable Object o) { 2279 if (!(o instanceof ScheduleInfo)) return false; 2280 final ScheduleInfo other = (ScheduleInfo) o; 2281 return toDayList(days).equals(toDayList(other.days)) 2282 && startHour == other.startHour 2283 && startMinute == other.startMinute 2284 && endHour == other.endHour 2285 && endMinute == other.endMinute 2286 && exitAtAlarm == other.exitAtAlarm; 2287 } 2288 copy()2289 public ScheduleInfo copy() { 2290 final ScheduleInfo rt = new ScheduleInfo(); 2291 if (days != null) { 2292 rt.days = new int[days.length]; 2293 System.arraycopy(days, 0, rt.days, 0, days.length); 2294 } 2295 rt.startHour = startHour; 2296 rt.startMinute = startMinute; 2297 rt.endHour = endHour; 2298 rt.endMinute = endMinute; 2299 rt.exitAtAlarm = exitAtAlarm; 2300 rt.nextAlarm = nextAlarm; 2301 return rt; 2302 } 2303 2304 @Override toString()2305 public String toString() { 2306 return "ScheduleInfo{" + 2307 "days=" + Arrays.toString(days) + 2308 ", startHour=" + startHour + 2309 ", startMinute=" + startMinute + 2310 ", endHour=" + endHour + 2311 ", endMinute=" + endMinute + 2312 ", exitAtAlarm=" + exitAtAlarm + 2313 ", nextAlarm=" + ts(nextAlarm) + 2314 '}'; 2315 } 2316 ts(long time)2317 protected static String ts(long time) { 2318 return new Date(time) + " (" + time + ")"; 2319 } 2320 } 2321 2322 // ==== Built-in system condition: event ==== 2323 2324 public static final String EVENT_PATH = "event"; 2325 toEventConditionId(EventInfo event)2326 public static Uri toEventConditionId(EventInfo event) { 2327 return new Uri.Builder().scheme(Condition.SCHEME) 2328 .authority(SYSTEM_AUTHORITY) 2329 .appendPath(EVENT_PATH) 2330 .appendQueryParameter("userId", Long.toString(event.userId)) 2331 .appendQueryParameter("calendar", event.calName != null ? event.calName : "") 2332 .appendQueryParameter("calendarId", event.calendarId != null 2333 ? event.calendarId.toString() : "") 2334 .appendQueryParameter("reply", Integer.toString(event.reply)) 2335 .build(); 2336 } 2337 isValidEventConditionId(Uri conditionId)2338 public static boolean isValidEventConditionId(Uri conditionId) { 2339 return tryParseEventConditionId(conditionId) != null; 2340 } 2341 2342 /** 2343 * Returns the {@link EventInfo} encoded in the condition id, or {@code null} if it could not be 2344 * decoded. 2345 */ 2346 @Nullable tryParseEventConditionId(Uri conditionId)2347 public static EventInfo tryParseEventConditionId(Uri conditionId) { 2348 final boolean isEvent = conditionId != null 2349 && Condition.SCHEME.equals(conditionId.getScheme()) 2350 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority()) 2351 && conditionId.getPathSegments().size() == 1 2352 && EVENT_PATH.equals(conditionId.getPathSegments().get(0)); 2353 if (!isEvent) return null; 2354 final EventInfo rt = new EventInfo(); 2355 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL); 2356 rt.calName = conditionId.getQueryParameter("calendar"); 2357 if (TextUtils.isEmpty(rt.calName)) { 2358 rt.calName = null; 2359 } 2360 rt.calendarId = tryParseLong(conditionId.getQueryParameter("calendarId"), null); 2361 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); 2362 return rt; 2363 } 2364 getEventConditionProvider()2365 public static ComponentName getEventConditionProvider() { 2366 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider"); 2367 } 2368 2369 public static class EventInfo { 2370 public static final int REPLY_ANY_EXCEPT_NO = 0; 2371 public static final int REPLY_YES_OR_MAYBE = 1; 2372 public static final int REPLY_YES = 2; 2373 2374 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user 2375 public String calName; // CalendarContract.Calendars.DISPLAY_NAME, or null for any 2376 public Long calendarId; // Calendars._ID, or null if restored from < Q calendar 2377 public int reply; 2378 2379 @Override hashCode()2380 public int hashCode() { 2381 return Objects.hash(userId, calName, calendarId, reply); 2382 } 2383 2384 @Override equals(@ullable Object o)2385 public boolean equals(@Nullable Object o) { 2386 if (!(o instanceof EventInfo)) return false; 2387 final EventInfo other = (EventInfo) o; 2388 return userId == other.userId 2389 && Objects.equals(calName, other.calName) 2390 && reply == other.reply 2391 && Objects.equals(calendarId, other.calendarId); 2392 } 2393 copy()2394 public EventInfo copy() { 2395 final EventInfo rt = new EventInfo(); 2396 rt.userId = userId; 2397 rt.calName = calName; 2398 rt.reply = reply; 2399 rt.calendarId = calendarId; 2400 return rt; 2401 } 2402 resolveUserId(int userId)2403 public static int resolveUserId(int userId) { 2404 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId; 2405 } 2406 } 2407 2408 // ==== End built-in system conditions ==== 2409 tryParseHourAndMinute(String value)2410 private static int[] tryParseHourAndMinute(String value) { 2411 if (TextUtils.isEmpty(value)) return null; 2412 final int i = value.indexOf('.'); 2413 if (i < 1 || i >= value.length() - 1) return null; 2414 final int hour = tryParseInt(value.substring(0, i), -1); 2415 final int minute = tryParseInt(value.substring(i + 1), -1); 2416 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null; 2417 } 2418 tryParseZenMode(String value, int defValue)2419 private static int tryParseZenMode(String value, int defValue) { 2420 final int rt = tryParseInt(value, defValue); 2421 return Global.isValidZenMode(rt) ? rt : defValue; 2422 } 2423 newRuleId()2424 public static String newRuleId() { 2425 return UUID.randomUUID().toString().replace("-", ""); 2426 } 2427 2428 /** 2429 * Gets the name of the app associated with owner 2430 */ getOwnerCaption(Context context, String owner)2431 public static String getOwnerCaption(Context context, String owner) { 2432 final PackageManager pm = context.getPackageManager(); 2433 try { 2434 final ApplicationInfo info = pm.getApplicationInfo(owner, 0); 2435 if (info != null) { 2436 final CharSequence seq = info.loadLabel(pm); 2437 if (seq != null) { 2438 final String str = seq.toString().trim(); 2439 if (str.length() > 0) { 2440 return str; 2441 } 2442 } 2443 } 2444 } catch (Throwable e) { 2445 Slog.w(TAG, "Error loading owner caption", e); 2446 } 2447 return ""; 2448 } 2449 isManualActive()2450 public boolean isManualActive() { 2451 if (!Flags.modesUi()) { 2452 return manualRule != null; 2453 } 2454 return manualRule != null && manualRule.isAutomaticActive(); 2455 } 2456 2457 public static class ZenRule implements Parcelable { 2458 @UnsupportedAppUsage 2459 public boolean enabled; 2460 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2461 public boolean snoozing; // user manually disabled this instance 2462 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2463 public String name; // required for automatic 2464 @UnsupportedAppUsage 2465 public int zenMode; // ie: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 2466 @UnsupportedAppUsage 2467 public Uri conditionId; // required for automatic 2468 public Condition condition; // optional 2469 public ComponentName component; // optional 2470 public ComponentName configurationActivity; // optional 2471 public String id; // required for automatic (unique) 2472 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 2473 public long creationTime; // required for automatic 2474 // package name, only used for manual rules when they have turned DND on. 2475 public String enabler; 2476 public ZenPolicy zenPolicy; 2477 @FlaggedApi(Flags.FLAG_MODES_API) 2478 @Nullable public ZenDeviceEffects zenDeviceEffects; 2479 public boolean modified; // rule has been modified from initial creation 2480 public String pkg; 2481 @AutomaticZenRule.Type 2482 public int type = AutomaticZenRule.TYPE_UNKNOWN; 2483 public String triggerDescription; 2484 public String iconResName; 2485 public boolean allowManualInvocation; 2486 @AutomaticZenRule.ModifiableField public int userModifiedFields; 2487 @ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields; 2488 @ZenDeviceEffects.ModifiableField public int zenDeviceEffectsUserModifiedFields; 2489 @Nullable public Instant deletionInstant; // Only set on deleted rules. 2490 ZenRule()2491 public ZenRule() { } 2492 ZenRule(Parcel source)2493 public ZenRule(Parcel source) { 2494 enabled = source.readInt() == 1; 2495 snoozing = source.readInt() == 1; 2496 if (source.readInt() == 1) { 2497 name = source.readString(); 2498 } 2499 zenMode = source.readInt(); 2500 conditionId = source.readParcelable(null, android.net.Uri.class); 2501 condition = source.readParcelable(null, android.service.notification.Condition.class); 2502 component = source.readParcelable(null, android.content.ComponentName.class); 2503 configurationActivity = source.readParcelable(null, android.content.ComponentName.class); 2504 if (source.readInt() == 1) { 2505 id = source.readString(); 2506 } 2507 creationTime = source.readLong(); 2508 if (source.readInt() == 1) { 2509 enabler = source.readString(); 2510 } 2511 zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); 2512 if (Flags.modesApi()) { 2513 zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class); 2514 } 2515 modified = source.readInt() == 1; 2516 pkg = source.readString(); 2517 if (Flags.modesApi()) { 2518 allowManualInvocation = source.readBoolean(); 2519 iconResName = source.readString(); 2520 triggerDescription = source.readString(); 2521 type = source.readInt(); 2522 userModifiedFields = source.readInt(); 2523 zenPolicyUserModifiedFields = source.readInt(); 2524 zenDeviceEffectsUserModifiedFields = source.readInt(); 2525 if (source.readInt() == 1) { 2526 deletionInstant = Instant.ofEpochMilli(source.readLong()); 2527 } 2528 } 2529 } 2530 2531 /** 2532 * Whether this ZenRule can be updated by an app. In general, rules that have been 2533 * customized by the user cannot be further updated by an app, with some exceptions: 2534 * <ul> 2535 * <li>Non user-configurable fields, like type, icon, configurationActivity, etc. 2536 * <li>Name, if the name was not specifically modified by the user (to support language 2537 * switches). 2538 * </ul> 2539 */ 2540 @FlaggedApi(Flags.FLAG_MODES_API) canBeUpdatedByApp()2541 public boolean canBeUpdatedByApp() { 2542 // The rule is considered updateable if its bitmask has no user modifications, and 2543 // the bitmasks of the policy and device effects have no modification. 2544 return userModifiedFields == 0 2545 && zenPolicyUserModifiedFields == 0 2546 && zenDeviceEffectsUserModifiedFields == 0; 2547 } 2548 2549 @Override describeContents()2550 public int describeContents() { 2551 return 0; 2552 } 2553 2554 @Override writeToParcel(Parcel dest, int flags)2555 public void writeToParcel(Parcel dest, int flags) { 2556 dest.writeInt(enabled ? 1 : 0); 2557 dest.writeInt(snoozing ? 1 : 0); 2558 if (name != null) { 2559 dest.writeInt(1); 2560 dest.writeString(name); 2561 } else { 2562 dest.writeInt(0); 2563 } 2564 dest.writeInt(zenMode); 2565 dest.writeParcelable(conditionId, 0); 2566 dest.writeParcelable(condition, 0); 2567 dest.writeParcelable(component, 0); 2568 dest.writeParcelable(configurationActivity, 0); 2569 if (id != null) { 2570 dest.writeInt(1); 2571 dest.writeString(id); 2572 } else { 2573 dest.writeInt(0); 2574 } 2575 dest.writeLong(creationTime); 2576 if (enabler != null) { 2577 dest.writeInt(1); 2578 dest.writeString(enabler); 2579 } else { 2580 dest.writeInt(0); 2581 } 2582 dest.writeParcelable(zenPolicy, 0); 2583 if (Flags.modesApi()) { 2584 dest.writeParcelable(zenDeviceEffects, 0); 2585 } 2586 dest.writeInt(modified ? 1 : 0); 2587 dest.writeString(pkg); 2588 if (Flags.modesApi()) { 2589 dest.writeBoolean(allowManualInvocation); 2590 dest.writeString(iconResName); 2591 dest.writeString(triggerDescription); 2592 dest.writeInt(type); 2593 dest.writeInt(userModifiedFields); 2594 dest.writeInt(zenPolicyUserModifiedFields); 2595 dest.writeInt(zenDeviceEffectsUserModifiedFields); 2596 if (deletionInstant != null) { 2597 dest.writeInt(1); 2598 dest.writeLong(deletionInstant.toEpochMilli()); 2599 } else { 2600 dest.writeInt(0); 2601 } 2602 } 2603 } 2604 2605 @Override toString()2606 public String toString() { 2607 StringBuilder sb = new StringBuilder(ZenRule.class.getSimpleName()).append('[') 2608 .append("id=").append(id) 2609 .append(",state=").append(condition == null ? "STATE_FALSE" 2610 : Condition.stateToString(condition.state)) 2611 .append(",enabled=").append(String.valueOf(enabled).toUpperCase()) 2612 .append(",snoozing=").append(snoozing) 2613 .append(",name=").append(name) 2614 .append(",zenMode=").append(Global.zenModeToString(zenMode)) 2615 .append(",conditionId=").append(conditionId) 2616 .append(",pkg=").append(pkg) 2617 .append(",component=").append(component) 2618 .append(",configActivity=").append(configurationActivity) 2619 .append(",creationTime=").append(creationTime) 2620 .append(",enabler=").append(enabler) 2621 .append(",zenPolicy=").append(zenPolicy) 2622 .append(",modified=").append(modified) 2623 .append(",condition=").append(condition); 2624 2625 if (Flags.modesApi()) { 2626 sb.append(",deviceEffects=").append(zenDeviceEffects) 2627 .append(",allowManualInvocation=").append(allowManualInvocation) 2628 .append(",iconResName=").append(iconResName) 2629 .append(",triggerDescription=").append(triggerDescription) 2630 .append(",type=").append(type); 2631 if (userModifiedFields != 0) { 2632 sb.append(",userModifiedFields=") 2633 .append(AutomaticZenRule.fieldsToString(userModifiedFields)); 2634 } 2635 if (zenPolicyUserModifiedFields != 0) { 2636 sb.append(",zenPolicyUserModifiedFields=") 2637 .append(ZenPolicy.fieldsToString(zenPolicyUserModifiedFields)); 2638 } 2639 if (zenDeviceEffectsUserModifiedFields != 0) { 2640 sb.append(",zenDeviceEffectsUserModifiedFields=") 2641 .append(ZenDeviceEffects.fieldsToString( 2642 zenDeviceEffectsUserModifiedFields)); 2643 } 2644 if (deletionInstant != null) { 2645 sb.append(",deletionInstant=").append(deletionInstant); 2646 } 2647 } 2648 2649 return sb.append(']').toString(); 2650 } 2651 2652 /** @hide */ 2653 // TODO: add configuration activity dumpDebug(ProtoOutputStream proto, long fieldId)2654 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 2655 final long token = proto.start(fieldId); 2656 2657 proto.write(ZenRuleProto.ID, id); 2658 proto.write(ZenRuleProto.NAME, name); 2659 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime); 2660 proto.write(ZenRuleProto.ENABLED, enabled); 2661 proto.write(ZenRuleProto.ENABLER, enabler); 2662 proto.write(ZenRuleProto.IS_SNOOZING, snoozing); 2663 proto.write(ZenRuleProto.ZEN_MODE, zenMode); 2664 if (conditionId != null) { 2665 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString()); 2666 } 2667 if (condition != null) { 2668 condition.dumpDebug(proto, ZenRuleProto.CONDITION); 2669 } 2670 if (component != null) { 2671 component.dumpDebug(proto, ZenRuleProto.COMPONENT); 2672 } 2673 if (zenPolicy != null) { 2674 zenPolicy.dumpDebug(proto, ZenRuleProto.ZEN_POLICY); 2675 } 2676 proto.write(ZenRuleProto.MODIFIED, modified); 2677 proto.end(token); 2678 } 2679 2680 @Override equals(@ullable Object o)2681 public boolean equals(@Nullable Object o) { 2682 if (!(o instanceof ZenRule)) return false; 2683 if (o == this) return true; 2684 final ZenRule other = (ZenRule) o; 2685 boolean finalEquals = other.enabled == enabled 2686 && other.snoozing == snoozing 2687 && Objects.equals(other.name, name) 2688 && other.zenMode == zenMode 2689 && Objects.equals(other.conditionId, conditionId) 2690 && Objects.equals(other.condition, condition) 2691 && Objects.equals(other.component, component) 2692 && Objects.equals(other.configurationActivity, configurationActivity) 2693 && Objects.equals(other.id, id) 2694 && Objects.equals(other.enabler, enabler) 2695 && Objects.equals(other.zenPolicy, zenPolicy) 2696 && Objects.equals(other.pkg, pkg) 2697 && other.modified == modified; 2698 2699 if (Flags.modesApi()) { 2700 return finalEquals 2701 && Objects.equals(other.zenDeviceEffects, zenDeviceEffects) 2702 && other.allowManualInvocation == allowManualInvocation 2703 && Objects.equals(other.iconResName, iconResName) 2704 && Objects.equals(other.triggerDescription, triggerDescription) 2705 && other.type == type 2706 && other.userModifiedFields == userModifiedFields 2707 && other.zenPolicyUserModifiedFields == zenPolicyUserModifiedFields 2708 && other.zenDeviceEffectsUserModifiedFields 2709 == zenDeviceEffectsUserModifiedFields 2710 && Objects.equals(other.deletionInstant, deletionInstant); 2711 } 2712 2713 return finalEquals; 2714 } 2715 2716 @Override hashCode()2717 public int hashCode() { 2718 if (Flags.modesApi()) { 2719 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, 2720 component, configurationActivity, pkg, id, enabler, zenPolicy, 2721 zenDeviceEffects, modified, allowManualInvocation, iconResName, 2722 triggerDescription, type, userModifiedFields, zenPolicyUserModifiedFields, 2723 zenDeviceEffectsUserModifiedFields, deletionInstant); 2724 } 2725 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, 2726 component, configurationActivity, pkg, id, enabler, zenPolicy, modified); 2727 } 2728 2729 /** Returns a deep copy of the {@link ZenRule}. */ copy()2730 public ZenRule copy() { 2731 final Parcel parcel = Parcel.obtain(); 2732 try { 2733 writeToParcel(parcel, 0); 2734 parcel.setDataPosition(0); 2735 return new ZenRule(parcel); 2736 } finally { 2737 parcel.recycle(); 2738 } 2739 } 2740 isAutomaticActive()2741 public boolean isAutomaticActive() { 2742 return enabled && !snoozing && getPkg() != null && isTrueOrUnknown(); 2743 } 2744 getPkg()2745 public String getPkg() { 2746 return !TextUtils.isEmpty(pkg) 2747 ? pkg 2748 : (component != null) 2749 ? component.getPackageName() 2750 : (configurationActivity != null) 2751 ? configurationActivity.getPackageName() 2752 : null; 2753 } 2754 isTrueOrUnknown()2755 public boolean isTrueOrUnknown() { 2756 return condition != null && (condition.state == Condition.STATE_TRUE 2757 || condition.state == Condition.STATE_UNKNOWN); 2758 } 2759 2760 public static final @android.annotation.NonNull Parcelable.Creator<ZenRule> CREATOR 2761 = new Parcelable.Creator<ZenRule>() { 2762 @Override 2763 public ZenRule createFromParcel(Parcel source) { 2764 return new ZenRule(source); 2765 } 2766 @Override 2767 public ZenRule[] newArray(int size) { 2768 return new ZenRule[size]; 2769 } 2770 }; 2771 } 2772 2773 /** 2774 * Determines whether dnd behavior should mute all ringer-controlled sounds 2775 * This includes notification, ringer and system sounds 2776 */ areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy policy)2777 public static boolean areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy 2778 policy) { 2779 boolean allowReminders = (policy.priorityCategories 2780 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0; 2781 boolean allowCalls = (policy.priorityCategories 2782 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0; 2783 boolean allowMessages = (policy.priorityCategories 2784 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0; 2785 boolean allowEvents = (policy.priorityCategories 2786 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0; 2787 boolean allowRepeatCallers = (policy.priorityCategories 2788 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0; 2789 boolean allowConversations = (policy.priorityConversationSenders 2790 & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; 2791 boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; 2792 if (Flags.modesApi()) { 2793 areChannelsBypassingDnd = policy.hasPriorityChannels() 2794 && policy.allowPriorityChannels(); 2795 } 2796 boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; 2797 return !allowReminders && !allowCalls && !allowMessages && !allowEvents 2798 && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem 2799 && !allowConversations; 2800 } 2801 2802 /** 2803 * Determines whether dnd behavior should mute all sounds 2804 */ areAllZenBehaviorSoundsMuted(NotificationManager.Policy policy)2805 public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy 2806 policy) { 2807 boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; 2808 boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; 2809 return !allowAlarms && !allowMedia && areAllPriorityOnlyRingerSoundsMuted(policy); 2810 } 2811 2812 /** 2813 * Determines if DND is currently overriding the ringer 2814 */ isZenOverridingRinger(int zen, Policy consolidatedPolicy)2815 public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) { 2816 return zen == Global.ZEN_MODE_NO_INTERRUPTIONS 2817 || zen == Global.ZEN_MODE_ALARMS 2818 || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS 2819 && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy)); 2820 } 2821 2822 /** 2823 * Determines whether dnd behavior should mute all ringer-controlled sounds 2824 * This includes notification, ringer and system sounds 2825 */ areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config)2826 public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) { 2827 if (Flags.modesUi()) { 2828 final ZenPolicy policy = config.manualRule.zenPolicy; 2829 return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false) 2830 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false) 2831 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false) 2832 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false) 2833 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false) 2834 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false) 2835 && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed() 2836 == STATE_ALLOW); 2837 2838 } else { 2839 boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd; 2840 if (Flags.modesApi()) { 2841 areChannelsBypassingDnd = config.areChannelsBypassingDnd 2842 && config.isAllowPriorityChannels(); 2843 } 2844 return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages() 2845 && !config.isAllowEvents() && !config.isAllowRepeatCallers() 2846 && !areChannelsBypassingDnd && !config.isAllowSystem(); 2847 } 2848 } 2849 2850 /** 2851 * Determines whether dnd mutes all sounds 2852 */ areAllZenBehaviorSoundsMuted(ZenModeConfig config)2853 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) { 2854 if (Flags.modesUi()) { 2855 final ZenPolicy policy = config.manualRule.zenPolicy; 2856 return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false) 2857 && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false) 2858 && areAllPriorityOnlyRingerSoundsMuted(config); 2859 } 2860 return !config.isAllowAlarms() && !config.isAllowMedia() 2861 && areAllPriorityOnlyRingerSoundsMuted(config); 2862 } 2863 2864 /** 2865 * Returns a description of the current do not disturb settings from config. 2866 * - If turned on manually and end time is known, returns end time. 2867 * - If turned on manually and end time is on forever until turned off, return null if 2868 * describeForeverCondition is false, else return String describing indefinite behavior 2869 * - If turned on by an automatic rule, returns the automatic rule name. 2870 * - If on due to an app, returns the app name. 2871 * - If there's a combination of rules/apps that trigger, then shows the one that will 2872 * last the longest if applicable. 2873 * @return null if DND is off or describeForeverCondition is false and 2874 * DND is on forever (until turned off) 2875 */ getDescription(Context context, boolean zenOn, ZenModeConfig config, boolean describeForeverCondition)2876 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config, 2877 boolean describeForeverCondition) { 2878 if (!zenOn || config == null) { 2879 return null; 2880 } 2881 2882 String secondaryText = ""; 2883 long latestEndTime = -1; 2884 2885 // DND turned on by manual rule 2886 if (config.manualRule != null) { 2887 final Uri id = config.manualRule.conditionId; 2888 if (config.manualRule.enabler != null) { 2889 // app triggered manual rule 2890 String appName = getOwnerCaption(context, config.manualRule.enabler); 2891 if (!appName.isEmpty()) { 2892 secondaryText = appName; 2893 } 2894 } else { 2895 if (id == null) { 2896 // Do not disturb manually triggered to remain on forever until turned off 2897 if (describeForeverCondition) { 2898 return context.getString(R.string.zen_mode_forever); 2899 } else { 2900 return null; 2901 } 2902 } else { 2903 latestEndTime = tryParseCountdownConditionId(id); 2904 if (latestEndTime > 0) { 2905 final CharSequence formattedTime = getFormattedTime(context, 2906 latestEndTime, isToday(latestEndTime), 2907 context.getUserId()); 2908 secondaryText = context.getString(R.string.zen_mode_until, formattedTime); 2909 } 2910 } 2911 } 2912 } 2913 2914 // DND turned on by an automatic rule 2915 for (ZenRule automaticRule : config.automaticRules.values()) { 2916 if (automaticRule.isAutomaticActive()) { 2917 if (isValidEventConditionId(automaticRule.conditionId) 2918 || isValidScheduleConditionId(automaticRule.conditionId)) { 2919 // set text if automatic rule end time is the latest active rule end time 2920 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId); 2921 if (endTime > latestEndTime) { 2922 latestEndTime = endTime; 2923 secondaryText = automaticRule.name; 2924 } 2925 } else { 2926 // set text if 3rd party rule 2927 return automaticRule.name; 2928 } 2929 } 2930 } 2931 2932 return !secondaryText.equals("") ? secondaryText : null; 2933 } 2934 parseAutomaticRuleEndTime(Context context, Uri id)2935 private static long parseAutomaticRuleEndTime(Context context, Uri id) { 2936 if (isValidEventConditionId(id)) { 2937 // cannot look up end times for events 2938 return Long.MAX_VALUE; 2939 } 2940 2941 if (isValidScheduleConditionId(id)) { 2942 ScheduleCalendar schedule = toScheduleCalendar(id); 2943 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis()); 2944 2945 // check if automatic rule will end on next alarm 2946 if (schedule.exitAtAlarm()) { 2947 long nextAlarm = getNextAlarm(context); 2948 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm); 2949 if (schedule.shouldExitForAlarm(endTimeMs)) { 2950 return nextAlarm; 2951 } 2952 } 2953 2954 return endTimeMs; 2955 } 2956 2957 return -1; 2958 } 2959 getNextAlarm(Context context)2960 private static long getNextAlarm(Context context) { 2961 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 2962 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId()); 2963 return info != null ? info.getTriggerTime() : 0; 2964 } 2965 } 2966