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