1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.notification; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.Notification; 23 import android.app.NotificationChannel; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.util.proto.ProtoOutputStream; 27 28 import java.io.ByteArrayOutputStream; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.Objects; 34 35 /** 36 * ZenPolicy determines whether to allow certain notifications and their corresponding sounds to 37 * play when a device is in Do Not Disturb mode. 38 * ZenPolicy also dictates the visual effects of notifications that are intercepted when 39 * a device is in Do Not Disturb mode. 40 */ 41 public final class ZenPolicy implements Parcelable { 42 private ArrayList<Integer> mPriorityCategories; 43 private ArrayList<Integer> mVisualEffects; 44 private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET; 45 private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET; 46 private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET; 47 48 /** @hide */ 49 @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = { 50 PRIORITY_CATEGORY_REMINDERS, 51 PRIORITY_CATEGORY_EVENTS, 52 PRIORITY_CATEGORY_MESSAGES, 53 PRIORITY_CATEGORY_CALLS, 54 PRIORITY_CATEGORY_REPEAT_CALLERS, 55 PRIORITY_CATEGORY_ALARMS, 56 PRIORITY_CATEGORY_MEDIA, 57 PRIORITY_CATEGORY_SYSTEM, 58 PRIORITY_CATEGORY_CONVERSATIONS, 59 }) 60 @Retention(RetentionPolicy.SOURCE) 61 public @interface PriorityCategory {} 62 63 /** @hide */ 64 public static final int PRIORITY_CATEGORY_REMINDERS = 0; 65 /** @hide */ 66 public static final int PRIORITY_CATEGORY_EVENTS = 1; 67 /** @hide */ 68 public static final int PRIORITY_CATEGORY_MESSAGES = 2; 69 /** @hide */ 70 public static final int PRIORITY_CATEGORY_CALLS = 3; 71 /** @hide */ 72 public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4; 73 /** @hide */ 74 public static final int PRIORITY_CATEGORY_ALARMS = 5; 75 /** @hide */ 76 public static final int PRIORITY_CATEGORY_MEDIA = 6; 77 /** @hide */ 78 public static final int PRIORITY_CATEGORY_SYSTEM = 7; 79 /** @hide */ 80 public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8; 81 82 /** @hide */ 83 @IntDef(prefix = { "VISUAL_EFFECT_" }, value = { 84 VISUAL_EFFECT_FULL_SCREEN_INTENT, 85 VISUAL_EFFECT_LIGHTS, 86 VISUAL_EFFECT_PEEK, 87 VISUAL_EFFECT_STATUS_BAR, 88 VISUAL_EFFECT_BADGE, 89 VISUAL_EFFECT_AMBIENT, 90 VISUAL_EFFECT_NOTIFICATION_LIST, 91 }) 92 @Retention(RetentionPolicy.SOURCE) 93 public @interface VisualEffect {} 94 95 /** @hide */ 96 public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0; 97 /** @hide */ 98 public static final int VISUAL_EFFECT_LIGHTS = 1; 99 /** @hide */ 100 public static final int VISUAL_EFFECT_PEEK = 2; 101 /** @hide */ 102 public static final int VISUAL_EFFECT_STATUS_BAR = 3; 103 /** @hide */ 104 public static final int VISUAL_EFFECT_BADGE = 4; 105 /** @hide */ 106 public static final int VISUAL_EFFECT_AMBIENT = 5; 107 /** @hide */ 108 public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6; 109 110 /** @hide */ 111 @IntDef(prefix = { "PEOPLE_TYPE_" }, value = { 112 PEOPLE_TYPE_UNSET, 113 PEOPLE_TYPE_ANYONE, 114 PEOPLE_TYPE_CONTACTS, 115 PEOPLE_TYPE_STARRED, 116 PEOPLE_TYPE_NONE, 117 }) 118 @Retention(RetentionPolicy.SOURCE) 119 public @interface PeopleType {} 120 121 /** 122 * Used to indicate no preference for the type of people that can bypass dnd for either 123 * calls or messages. 124 */ 125 public static final int PEOPLE_TYPE_UNSET = 0; 126 127 /** 128 * Used to indicate all calls or messages can bypass dnd. 129 */ 130 public static final int PEOPLE_TYPE_ANYONE = 1; 131 132 /** 133 * Used to indicate calls or messages from contacts can bypass dnd. 134 */ 135 public static final int PEOPLE_TYPE_CONTACTS = 2; 136 137 /** 138 * Used to indicate calls or messages from starred contacts can bypass dnd. 139 */ 140 public static final int PEOPLE_TYPE_STARRED = 3; 141 142 /** 143 * Used to indicate no calls or messages can bypass dnd. 144 */ 145 public static final int PEOPLE_TYPE_NONE = 4; 146 147 148 /** @hide */ 149 @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = { 150 CONVERSATION_SENDERS_UNSET, 151 CONVERSATION_SENDERS_ANYONE, 152 CONVERSATION_SENDERS_IMPORTANT, 153 CONVERSATION_SENDERS_NONE, 154 }) 155 @Retention(RetentionPolicy.SOURCE) 156 public @interface ConversationSenders {} 157 158 /** 159 * Used to indicate no preference for the type of conversations that can bypass dnd. 160 */ 161 public static final int CONVERSATION_SENDERS_UNSET = 0; 162 163 /** 164 * Used to indicate all conversations can bypass dnd. 165 */ 166 public static final int CONVERSATION_SENDERS_ANYONE = 1; 167 168 /** 169 * Used to indicate important conversations can bypass dnd. 170 */ 171 public static final int CONVERSATION_SENDERS_IMPORTANT = 2; 172 173 /** 174 * Used to indicate no conversations can bypass dnd. 175 */ 176 public static final int CONVERSATION_SENDERS_NONE = 3; 177 178 /** @hide */ 179 @IntDef(prefix = { "STATE_" }, value = { 180 STATE_UNSET, 181 STATE_ALLOW, 182 STATE_DISALLOW, 183 }) 184 @Retention(RetentionPolicy.SOURCE) 185 public @interface State {} 186 187 /** 188 * Indicates no preference for whether a type of sound or visual effect is or isn't allowed 189 * to play/show when DND is active. Will default to the current set policy. 190 */ 191 public static final int STATE_UNSET = 0; 192 193 /** 194 * Indicates a type of sound or visual effect is allowed to play/show when DND is active. 195 */ 196 public static final int STATE_ALLOW = 1; 197 198 /** 199 * Indicates a type of sound or visual effect is not allowed to play/show when DND is active. 200 */ 201 public static final int STATE_DISALLOW = 2; 202 203 /** @hide */ ZenPolicy()204 public ZenPolicy() { 205 mPriorityCategories = new ArrayList<>(Collections.nCopies(9, 0)); 206 mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0)); 207 } 208 209 /** 210 * Conversation type that can bypass DND. 211 * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE}, 212 * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}. 213 */ getPriorityConversationSenders()214 public @PeopleType int getPriorityConversationSenders() { 215 return mConversationSenders; 216 } 217 218 /** 219 * Message senders that can bypass DND. 220 * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, 221 * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} 222 */ getPriorityMessageSenders()223 public @PeopleType int getPriorityMessageSenders() { 224 return mPriorityMessages; 225 } 226 227 /** 228 * Callers that can bypass DND. 229 * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, 230 * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} 231 */ getPriorityCallSenders()232 public @PeopleType int getPriorityCallSenders() { 233 return mPriorityCalls; 234 } 235 236 /** 237 * Whether this policy wants to allow conversation notifications 238 * (see {@link NotificationChannel#getConversationId()}) to play sounds and visually appear 239 * or to intercept them when DND is active. 240 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 241 */ getPriorityCategoryConversations()242 public @State int getPriorityCategoryConversations() { 243 return mPriorityCategories.get(PRIORITY_CATEGORY_CONVERSATIONS); 244 } 245 246 /** 247 * Whether this policy wants to allow notifications with category 248 * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear 249 * or to intercept them when DND is active. 250 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 251 */ getPriorityCategoryReminders()252 public @State int getPriorityCategoryReminders() { 253 return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS); 254 } 255 256 /** 257 * Whether this policy wants to allow notifications with category 258 * {@link Notification#CATEGORY_EVENT} to play sounds and visually appear 259 * or to intercept them when DND is active. 260 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 261 */ getPriorityCategoryEvents()262 public @State int getPriorityCategoryEvents() { 263 return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS); 264 } 265 266 /** 267 * Whether this policy wants to allow notifications with category 268 * {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear 269 * or to intercept them when DND is active. Types of message senders that are allowed 270 * are specified by {@link #getPriorityMessageSenders}. 271 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 272 */ getPriorityCategoryMessages()273 public @State int getPriorityCategoryMessages() { 274 return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES); 275 } 276 277 /** 278 * Whether this policy wants to allow notifications with category 279 * {@link Notification#CATEGORY_CALL} to play sounds and visually appear 280 * or to intercept them when DND is active. Types of callers that are allowed 281 * are specified by {@link #getPriorityCallSenders()}. 282 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 283 */ getPriorityCategoryCalls()284 public @State int getPriorityCategoryCalls() { 285 return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS); 286 } 287 288 /** 289 * Whether this policy wants to allow repeat callers (notifications with category 290 * {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and 291 * visually appear or to intercept them when DND is active. 292 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 293 */ getPriorityCategoryRepeatCallers()294 public @State int getPriorityCategoryRepeatCallers() { 295 return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS); 296 } 297 298 /** 299 * Whether this policy wants to allow notifications with category 300 * {@link Notification#CATEGORY_ALARM} to play sounds and visually appear 301 * or to intercept them when DND is active. 302 * When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND 303 * is active. 304 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 305 */ getPriorityCategoryAlarms()306 public @State int getPriorityCategoryAlarms() { 307 return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS); 308 } 309 310 /** 311 * Whether this policy wants to allow media notifications to play sounds and visually appear 312 * or to intercept them when DND is active. 313 * When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is 314 * active. 315 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 316 */ getPriorityCategoryMedia()317 public @State int getPriorityCategoryMedia() { 318 return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA); 319 } 320 321 /** 322 * Whether this policy wants to allow system sounds when DND is active. 323 * When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active. 324 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 325 */ getPriorityCategorySystem()326 public @State int getPriorityCategorySystem() { 327 return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM); 328 } 329 330 /** 331 * Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from 332 * notifications intercepted by DND. 333 */ getVisualEffectFullScreenIntent()334 public @State int getVisualEffectFullScreenIntent() { 335 return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT); 336 } 337 338 /** 339 * Whether this policy allows {@link NotificationChannel#shouldShowLights() notification 340 * lights} from notifications intercepted by DND. 341 */ getVisualEffectLights()342 public @State int getVisualEffectLights() { 343 return mVisualEffects.get(VISUAL_EFFECT_LIGHTS); 344 } 345 346 /** 347 * Whether this policy allows peeking from notifications intercepted by DND. 348 */ getVisualEffectPeek()349 public @State int getVisualEffectPeek() { 350 return mVisualEffects.get(VISUAL_EFFECT_PEEK); 351 } 352 353 /** 354 * Whether this policy allows notifications intercepted by DND from appearing in the status bar 355 * on devices that support status bars. 356 */ getVisualEffectStatusBar()357 public @State int getVisualEffectStatusBar() { 358 return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR); 359 } 360 361 /** 362 * Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from 363 * notifications intercepted by DND on devices that support badging. 364 */ getVisualEffectBadge()365 public @State int getVisualEffectBadge() { 366 return mVisualEffects.get(VISUAL_EFFECT_BADGE); 367 } 368 369 /** 370 * Whether this policy allows notifications intercepted by DND from appearing on ambient 371 * displays on devices that support ambient display. 372 */ getVisualEffectAmbient()373 public @State int getVisualEffectAmbient() { 374 return mVisualEffects.get(VISUAL_EFFECT_AMBIENT); 375 } 376 377 /** 378 * Whether this policy allows notifications intercepted by DND from appearing in notification 379 * list views like the notification shade or lockscreen on devices that support those 380 * views. 381 */ getVisualEffectNotificationList()382 public @State int getVisualEffectNotificationList() { 383 return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST); 384 } 385 386 /** 387 * Whether this policy hides all visual effects 388 * @hide 389 */ shouldHideAllVisualEffects()390 public boolean shouldHideAllVisualEffects() { 391 for (int i = 0; i < mVisualEffects.size(); i++) { 392 if (mVisualEffects.get(i) != STATE_DISALLOW) { 393 return false; 394 } 395 } 396 return true; 397 } 398 399 /** 400 * Whether this policy shows all visual effects 401 * @hide 402 */ shouldShowAllVisualEffects()403 public boolean shouldShowAllVisualEffects() { 404 for (int i = 0; i < mVisualEffects.size(); i++) { 405 if (mVisualEffects.get(i) != STATE_ALLOW) { 406 return false; 407 } 408 } 409 return true; 410 } 411 412 /** 413 * Builder class for {@link ZenPolicy} objects. 414 * Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field 415 * is not set, it is (@link STATE_UNSET} and will not change the current set policy. 416 */ 417 public static final class Builder { 418 private ZenPolicy mZenPolicy; 419 Builder()420 public Builder() { 421 mZenPolicy = new ZenPolicy(); 422 } 423 424 /** 425 * @hide 426 */ Builder(ZenPolicy policy)427 public Builder(ZenPolicy policy) { 428 if (policy != null) { 429 mZenPolicy = policy.copy(); 430 } else { 431 mZenPolicy = new ZenPolicy(); 432 } 433 } 434 435 /** 436 * Builds the current ZenPolicy. 437 */ build()438 public @NonNull ZenPolicy build() { 439 return mZenPolicy.copy(); 440 } 441 442 /** 443 * Allows all notifications to bypass DND and unmutes all streams. 444 */ allowAllSounds()445 public @NonNull Builder allowAllSounds() { 446 for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { 447 mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW); 448 } 449 mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE; 450 mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE; 451 mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_ANYONE; 452 return this; 453 } 454 455 /** 456 * Intercepts all notifications and prevents them from playing sounds 457 * when DND is active. Also mutes alarm, system and media streams. 458 * Notification channels can still play sounds only if they 459 * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND, 460 * the ringer stream is also muted. 461 */ disallowAllSounds()462 public @NonNull Builder disallowAllSounds() { 463 for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { 464 mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW); 465 } 466 mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE; 467 mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE; 468 mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE; 469 return this; 470 } 471 472 /** 473 * Allows notifications intercepted by DND to show on all surfaces when DND is active. 474 */ showAllVisualEffects()475 public @NonNull Builder showAllVisualEffects() { 476 for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { 477 mZenPolicy.mVisualEffects.set(i, STATE_ALLOW); 478 } 479 return this; 480 } 481 482 /** 483 * Disallows notifications intercepted by DND from showing when DND is active. 484 */ hideAllVisualEffects()485 public @NonNull Builder hideAllVisualEffects() { 486 for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { 487 mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW); 488 } 489 return this; 490 } 491 492 /** 493 * Unsets a priority category, neither allowing or disallowing. When applying this policy, 494 * unset categories will default to the current applied policy. 495 * @hide 496 */ unsetPriorityCategory(@riorityCategory int category)497 public @NonNull Builder unsetPriorityCategory(@PriorityCategory int category) { 498 mZenPolicy.mPriorityCategories.set(category, STATE_UNSET); 499 500 if (category == PRIORITY_CATEGORY_MESSAGES) { 501 mZenPolicy.mPriorityMessages = STATE_UNSET; 502 } else if (category == PRIORITY_CATEGORY_CALLS) { 503 mZenPolicy.mPriorityCalls = STATE_UNSET; 504 } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) { 505 mZenPolicy.mConversationSenders = STATE_UNSET; 506 } 507 508 return this; 509 } 510 511 /** 512 * Unsets a visual effect, neither allowing or disallowing. When applying this policy, 513 * unset effects will default to the current applied policy. 514 * @hide 515 */ unsetVisualEffect(@isualEffect int effect)516 public @NonNull Builder unsetVisualEffect(@VisualEffect int effect) { 517 mZenPolicy.mVisualEffects.set(effect, STATE_UNSET); 518 return this; 519 } 520 521 /** 522 * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER} 523 * to play sounds and visually appear or to intercept them when DND is active. 524 */ allowReminders(boolean allow)525 public @NonNull Builder allowReminders(boolean allow) { 526 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS, 527 allow ? STATE_ALLOW : STATE_DISALLOW); 528 return this; 529 } 530 531 /** 532 * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT} 533 * to play sounds and visually appear or to intercept them when DND is active. 534 */ allowEvents(boolean allow)535 public @NonNull Builder allowEvents(boolean allow) { 536 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS, 537 allow ? STATE_ALLOW : STATE_DISALLOW); 538 return this; 539 } 540 541 /** 542 * Whether to allow conversation notifications 543 * (see {@link NotificationChannel#setConversationId(String, String)}) 544 * that match audienceType to play sounds and visually appear or to intercept 545 * them when DND is active. 546 * @param audienceType callers that are allowed to bypass DND 547 */ allowConversations(@onversationSenders int audienceType)548 public @NonNull Builder allowConversations(@ConversationSenders int audienceType) { 549 if (audienceType == STATE_UNSET) { 550 return unsetPriorityCategory(PRIORITY_CATEGORY_CONVERSATIONS); 551 } 552 553 if (audienceType == CONVERSATION_SENDERS_NONE) { 554 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_DISALLOW); 555 } else if (audienceType == CONVERSATION_SENDERS_ANYONE 556 || audienceType == CONVERSATION_SENDERS_IMPORTANT) { 557 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_ALLOW); 558 } else { 559 return this; 560 } 561 562 mZenPolicy.mConversationSenders = audienceType; 563 return this; 564 } 565 566 /** 567 * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE} 568 * that match audienceType to play sounds and visually appear or to intercept 569 * them when DND is active. 570 * @param audienceType message senders that are allowed to bypass DND 571 */ allowMessages(@eopleType int audienceType)572 public @NonNull Builder allowMessages(@PeopleType int audienceType) { 573 if (audienceType == STATE_UNSET) { 574 return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES); 575 } 576 577 if (audienceType == PEOPLE_TYPE_NONE) { 578 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW); 579 } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS 580 || audienceType == PEOPLE_TYPE_STARRED) { 581 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW); 582 } else { 583 return this; 584 } 585 586 mZenPolicy.mPriorityMessages = audienceType; 587 return this; 588 } 589 590 /** 591 * Whether to allow notifications with category {@link Notification#CATEGORY_CALL} 592 * that match audienceType to play sounds and visually appear or to intercept 593 * them when DND is active. 594 * @param audienceType callers that are allowed to bypass DND 595 */ allowCalls(@eopleType int audienceType)596 public @NonNull Builder allowCalls(@PeopleType int audienceType) { 597 if (audienceType == STATE_UNSET) { 598 return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS); 599 } 600 601 if (audienceType == PEOPLE_TYPE_NONE) { 602 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW); 603 } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS 604 || audienceType == PEOPLE_TYPE_STARRED) { 605 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW); 606 } else { 607 return this; 608 } 609 610 mZenPolicy.mPriorityCalls = audienceType; 611 return this; 612 } 613 614 /** 615 * Whether to allow repeat callers (notifications with category 616 * {@link Notification#CATEGORY_CALL} that have recently called 617 * to play sounds and visually appear. 618 */ allowRepeatCallers(boolean allow)619 public @NonNull Builder allowRepeatCallers(boolean allow) { 620 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS, 621 allow ? STATE_ALLOW : STATE_DISALLOW); 622 return this; 623 } 624 625 /** 626 * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM} 627 * to play sounds and visually appear or to intercept them when DND is active. 628 * Disallowing alarms will mute the alarm stream when DND is active. 629 */ allowAlarms(boolean allow)630 public @NonNull Builder allowAlarms(boolean allow) { 631 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS, 632 allow ? STATE_ALLOW : STATE_DISALLOW); 633 return this; 634 } 635 636 /** 637 * Whether to allow media notifications to play sounds and visually 638 * appear or to intercept them when DND is active. 639 * Disallowing media will mute the media stream when DND is active. 640 */ allowMedia(boolean allow)641 public @NonNull Builder allowMedia(boolean allow) { 642 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA, 643 allow ? STATE_ALLOW : STATE_DISALLOW); 644 return this; 645 } 646 647 /** 648 * Whether to allow system sounds to play when DND is active. 649 * Disallowing system sounds will mute the system stream when DND is active. 650 */ allowSystem(boolean allow)651 public @NonNull Builder allowSystem(boolean allow) { 652 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM, 653 allow ? STATE_ALLOW : STATE_DISALLOW); 654 return this; 655 } 656 657 /** 658 * Whether to allow {@link PriorityCategory} sounds to play when DND is active. 659 * @hide 660 */ allowCategory(@riorityCategory int category, boolean allow)661 public @NonNull Builder allowCategory(@PriorityCategory int category, boolean allow) { 662 switch (category) { 663 case PRIORITY_CATEGORY_ALARMS: 664 allowAlarms(allow); 665 break; 666 case PRIORITY_CATEGORY_MEDIA: 667 allowMedia(allow); 668 break; 669 case PRIORITY_CATEGORY_SYSTEM: 670 allowSystem(allow); 671 break; 672 case PRIORITY_CATEGORY_REMINDERS: 673 allowReminders(allow); 674 break; 675 case PRIORITY_CATEGORY_EVENTS: 676 allowEvents(allow); 677 break; 678 case PRIORITY_CATEGORY_REPEAT_CALLERS: 679 allowRepeatCallers(allow); 680 break; 681 } 682 return this; 683 } 684 685 /** 686 * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted 687 * by DND are shown. 688 */ showFullScreenIntent(boolean show)689 public @NonNull Builder showFullScreenIntent(boolean show) { 690 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT, 691 show ? STATE_ALLOW : STATE_DISALLOW); 692 return this; 693 } 694 695 /** 696 * Whether {@link NotificationChannel#shouldShowLights() notification lights} from 697 * notifications intercepted by DND are blocked. 698 */ showLights(boolean show)699 public @NonNull Builder showLights(boolean show) { 700 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS, 701 show ? STATE_ALLOW : STATE_DISALLOW); 702 return this; 703 } 704 705 /** 706 * Whether notifications intercepted by DND are prevented from peeking. 707 */ showPeeking(boolean show)708 public @NonNull Builder showPeeking(boolean show) { 709 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK, 710 show ? STATE_ALLOW : STATE_DISALLOW); 711 return this; 712 } 713 714 /** 715 * Whether notifications intercepted by DND are prevented from appearing in the status bar 716 * on devices that support status bars. 717 */ showStatusBarIcons(boolean show)718 public @NonNull Builder showStatusBarIcons(boolean show) { 719 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR, 720 show ? STATE_ALLOW : STATE_DISALLOW); 721 return this; 722 } 723 724 /** 725 * Whether {@link NotificationChannel#canShowBadge() badges} from 726 * notifications intercepted by DND are allowed on devices that support badging. 727 */ showBadges(boolean show)728 public @NonNull Builder showBadges(boolean show) { 729 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE, 730 show ? STATE_ALLOW : STATE_DISALLOW); 731 return this; 732 } 733 734 /** 735 * Whether notification intercepted by DND are prevented from appearing on ambient displays 736 * on devices that support ambient display. 737 */ showInAmbientDisplay(boolean show)738 public @NonNull Builder showInAmbientDisplay(boolean show) { 739 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT, 740 show ? STATE_ALLOW : STATE_DISALLOW); 741 return this; 742 } 743 744 /** 745 * Whether notification intercepted by DND are prevented from appearing in notification 746 * list views like the notification shade or lockscreen on devices that support those 747 * views. 748 */ showInNotificationList(boolean show)749 public @NonNull Builder showInNotificationList(boolean show) { 750 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST, 751 show ? STATE_ALLOW : STATE_DISALLOW); 752 return this; 753 } 754 755 /** 756 * Whether notifications intercepted by DND are prevented from appearing for 757 * {@link VisualEffect} 758 * @hide 759 */ showVisualEffect(@isualEffect int effect, boolean show)760 public @NonNull Builder showVisualEffect(@VisualEffect int effect, boolean show) { 761 switch (effect) { 762 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 763 showFullScreenIntent(show); 764 break; 765 case VISUAL_EFFECT_LIGHTS: 766 showLights(show); 767 break; 768 case VISUAL_EFFECT_PEEK: 769 showPeeking(show); 770 break; 771 case VISUAL_EFFECT_STATUS_BAR: 772 showStatusBarIcons(show); 773 break; 774 case VISUAL_EFFECT_BADGE: 775 showBadges(show); 776 break; 777 case VISUAL_EFFECT_AMBIENT: 778 showInAmbientDisplay(show); 779 break; 780 case VISUAL_EFFECT_NOTIFICATION_LIST: 781 showInNotificationList(show); 782 break; 783 } 784 return this; 785 } 786 } 787 788 @Override describeContents()789 public int describeContents() { 790 return 0; 791 } 792 793 @Override writeToParcel(Parcel dest, int flags)794 public void writeToParcel(Parcel dest, int flags) { 795 dest.writeList(mPriorityCategories); 796 dest.writeList(mVisualEffects); 797 dest.writeInt(mPriorityCalls); 798 dest.writeInt(mPriorityMessages); 799 dest.writeInt(mConversationSenders); 800 } 801 802 public static final @android.annotation.NonNull Parcelable.Creator<ZenPolicy> CREATOR = 803 new Parcelable.Creator<ZenPolicy>() { 804 @Override 805 public ZenPolicy createFromParcel(Parcel source) { 806 ZenPolicy policy = new ZenPolicy(); 807 policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader()); 808 policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader()); 809 policy.mPriorityCalls = source.readInt(); 810 policy.mPriorityMessages = source.readInt(); 811 policy.mConversationSenders = source.readInt(); 812 return policy; 813 } 814 815 @Override 816 public ZenPolicy[] newArray(int size) { 817 return new ZenPolicy[size]; 818 } 819 }; 820 821 @Override toString()822 public String toString() { 823 return new StringBuilder(ZenPolicy.class.getSimpleName()) 824 .append('{') 825 .append("priorityCategories=[").append(priorityCategoriesToString()) 826 .append("], visualEffects=[").append(visualEffectsToString()) 827 .append("], priorityCallsSenders=").append(peopleTypeToString(mPriorityCalls)) 828 .append(", priorityMessagesSenders=").append(peopleTypeToString(mPriorityMessages)) 829 .append(", priorityConversationSenders=").append( 830 conversationTypeToString(mConversationSenders)) 831 .append('}') 832 .toString(); 833 } 834 835 priorityCategoriesToString()836 private String priorityCategoriesToString() { 837 StringBuilder builder = new StringBuilder(); 838 for (int i = 0; i < mPriorityCategories.size(); i++) { 839 if (mPriorityCategories.get(i) != STATE_UNSET) { 840 builder.append(indexToCategory(i)) 841 .append("=") 842 .append(stateToString(mPriorityCategories.get(i))) 843 .append(" "); 844 } 845 846 } 847 return builder.toString(); 848 } 849 visualEffectsToString()850 private String visualEffectsToString() { 851 StringBuilder builder = new StringBuilder(); 852 for (int i = 0; i < mVisualEffects.size(); i++) { 853 if (mVisualEffects.get(i) != STATE_UNSET) { 854 builder.append(indexToVisualEffect(i)) 855 .append("=") 856 .append(stateToString(mVisualEffects.get(i))) 857 .append(" "); 858 } 859 860 } 861 return builder.toString(); 862 } 863 indexToVisualEffect(@isualEffect int visualEffectIndex)864 private String indexToVisualEffect(@VisualEffect int visualEffectIndex) { 865 switch (visualEffectIndex) { 866 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 867 return "fullScreenIntent"; 868 case VISUAL_EFFECT_LIGHTS: 869 return "lights"; 870 case VISUAL_EFFECT_PEEK: 871 return "peek"; 872 case VISUAL_EFFECT_STATUS_BAR: 873 return "statusBar"; 874 case VISUAL_EFFECT_BADGE: 875 return "badge"; 876 case VISUAL_EFFECT_AMBIENT: 877 return "ambient"; 878 case VISUAL_EFFECT_NOTIFICATION_LIST: 879 return "notificationList"; 880 } 881 return null; 882 } 883 indexToCategory(@riorityCategory int categoryIndex)884 private String indexToCategory(@PriorityCategory int categoryIndex) { 885 switch (categoryIndex) { 886 case PRIORITY_CATEGORY_REMINDERS: 887 return "reminders"; 888 case PRIORITY_CATEGORY_EVENTS: 889 return "events"; 890 case PRIORITY_CATEGORY_MESSAGES: 891 return "messages"; 892 case PRIORITY_CATEGORY_CALLS: 893 return "calls"; 894 case PRIORITY_CATEGORY_REPEAT_CALLERS: 895 return "repeatCallers"; 896 case PRIORITY_CATEGORY_ALARMS: 897 return "alarms"; 898 case PRIORITY_CATEGORY_MEDIA: 899 return "media"; 900 case PRIORITY_CATEGORY_SYSTEM: 901 return "system"; 902 case PRIORITY_CATEGORY_CONVERSATIONS: 903 return "convs"; 904 } 905 return null; 906 } 907 stateToString(@tate int state)908 private String stateToString(@State int state) { 909 switch (state) { 910 case STATE_UNSET: 911 return "unset"; 912 case STATE_DISALLOW: 913 return "disallow"; 914 case STATE_ALLOW: 915 return "allow"; 916 } 917 return "invalidState{" + state + "}"; 918 } 919 peopleTypeToString(@eopleType int peopleType)920 private String peopleTypeToString(@PeopleType int peopleType) { 921 switch (peopleType) { 922 case PEOPLE_TYPE_ANYONE: 923 return "anyone"; 924 case PEOPLE_TYPE_CONTACTS: 925 return "contacts"; 926 case PEOPLE_TYPE_NONE: 927 return "none"; 928 case PEOPLE_TYPE_STARRED: 929 return "starred_contacts"; 930 case STATE_UNSET: 931 return "unset"; 932 } 933 return "invalidPeopleType{" + peopleType + "}"; 934 } 935 936 /** 937 * @hide 938 */ conversationTypeToString(@onversationSenders int conversationType)939 public static String conversationTypeToString(@ConversationSenders int conversationType) { 940 switch (conversationType) { 941 case CONVERSATION_SENDERS_ANYONE: 942 return "anyone"; 943 case CONVERSATION_SENDERS_IMPORTANT: 944 return "important"; 945 case CONVERSATION_SENDERS_NONE: 946 return "none"; 947 case CONVERSATION_SENDERS_UNSET: 948 return "unset"; 949 } 950 return "invalidConversationType{" + conversationType + "}"; 951 } 952 953 @Override equals(@ullable Object o)954 public boolean equals(@Nullable Object o) { 955 if (!(o instanceof ZenPolicy)) return false; 956 if (o == this) return true; 957 final ZenPolicy other = (ZenPolicy) o; 958 959 return Objects.equals(other.mPriorityCategories, mPriorityCategories) 960 && Objects.equals(other.mVisualEffects, mVisualEffects) 961 && other.mPriorityCalls == mPriorityCalls 962 && other.mPriorityMessages == mPriorityMessages 963 && other.mConversationSenders == mConversationSenders; 964 } 965 966 @Override hashCode()967 public int hashCode() { 968 return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages, 969 mConversationSenders); 970 } 971 getZenPolicyPriorityCategoryState(@riorityCategory int category)972 private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int 973 category) { 974 switch (category) { 975 case PRIORITY_CATEGORY_REMINDERS: 976 return getPriorityCategoryReminders(); 977 case PRIORITY_CATEGORY_EVENTS: 978 return getPriorityCategoryEvents(); 979 case PRIORITY_CATEGORY_MESSAGES: 980 return getPriorityCategoryMessages(); 981 case PRIORITY_CATEGORY_CALLS: 982 return getPriorityCategoryCalls(); 983 case PRIORITY_CATEGORY_REPEAT_CALLERS: 984 return getPriorityCategoryRepeatCallers(); 985 case PRIORITY_CATEGORY_ALARMS: 986 return getPriorityCategoryAlarms(); 987 case PRIORITY_CATEGORY_MEDIA: 988 return getPriorityCategoryMedia(); 989 case PRIORITY_CATEGORY_SYSTEM: 990 return getPriorityCategorySystem(); 991 case PRIORITY_CATEGORY_CONVERSATIONS: 992 return getPriorityCategoryConversations(); 993 } 994 return -1; 995 } 996 getZenPolicyVisualEffectState(@isualEffect int effect)997 private @ZenPolicy.State int getZenPolicyVisualEffectState(@VisualEffect int effect) { 998 switch (effect) { 999 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 1000 return getVisualEffectFullScreenIntent(); 1001 case VISUAL_EFFECT_LIGHTS: 1002 return getVisualEffectLights(); 1003 case VISUAL_EFFECT_PEEK: 1004 return getVisualEffectPeek(); 1005 case VISUAL_EFFECT_STATUS_BAR: 1006 return getVisualEffectStatusBar(); 1007 case VISUAL_EFFECT_BADGE: 1008 return getVisualEffectBadge(); 1009 case VISUAL_EFFECT_AMBIENT: 1010 return getVisualEffectAmbient(); 1011 case VISUAL_EFFECT_NOTIFICATION_LIST: 1012 return getVisualEffectNotificationList(); 1013 } 1014 return -1; 1015 } 1016 1017 /** @hide */ isCategoryAllowed(@riorityCategory int category, boolean defaultVal)1018 public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) { 1019 switch (getZenPolicyPriorityCategoryState(category)) { 1020 case ZenPolicy.STATE_ALLOW: 1021 return true; 1022 case ZenPolicy.STATE_DISALLOW: 1023 return false; 1024 default: 1025 return defaultVal; 1026 } 1027 } 1028 1029 /** @hide */ isVisualEffectAllowed(@isualEffect int effect, boolean defaultVal)1030 public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) { 1031 switch (getZenPolicyVisualEffectState(effect)) { 1032 case ZenPolicy.STATE_ALLOW: 1033 return true; 1034 case ZenPolicy.STATE_DISALLOW: 1035 return false; 1036 default: 1037 return defaultVal; 1038 } 1039 } 1040 1041 /** 1042 * Applies another policy on top of this policy 1043 * @hide 1044 */ apply(ZenPolicy policyToApply)1045 public void apply(ZenPolicy policyToApply) { 1046 if (policyToApply == null) { 1047 return; 1048 } 1049 1050 // apply priority categories 1051 for (int category = 0; category < mPriorityCategories.size(); category++) { 1052 if (mPriorityCategories.get(category) == STATE_DISALLOW) { 1053 // if a priority category is already disallowed by the policy, cannot allow 1054 continue; 1055 } 1056 1057 @State int newState = policyToApply.mPriorityCategories.get(category); 1058 if (newState != STATE_UNSET) { 1059 mPriorityCategories.set(category, newState); 1060 1061 if (category == PRIORITY_CATEGORY_MESSAGES 1062 && mPriorityMessages < policyToApply.mPriorityMessages) { 1063 mPriorityMessages = policyToApply.mPriorityMessages; 1064 } else if (category == PRIORITY_CATEGORY_CALLS 1065 && mPriorityCalls < policyToApply.mPriorityCalls) { 1066 mPriorityCalls = policyToApply.mPriorityCalls; 1067 } else if (category == PRIORITY_CATEGORY_CONVERSATIONS 1068 && mConversationSenders < policyToApply.mConversationSenders) { 1069 mConversationSenders = policyToApply.mConversationSenders; 1070 } 1071 } 1072 } 1073 1074 // apply visual effects 1075 for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) { 1076 if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) { 1077 // if a visual effect is already disallowed by the policy, cannot allow 1078 continue; 1079 } 1080 1081 if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) { 1082 mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect)); 1083 } 1084 } 1085 } 1086 1087 /** 1088 * @hide 1089 */ dumpDebug(ProtoOutputStream proto, long fieldId)1090 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 1091 final long token = proto.start(fieldId); 1092 1093 proto.write(ZenPolicyProto.REMINDERS, getPriorityCategoryReminders()); 1094 proto.write(ZenPolicyProto.EVENTS, getPriorityCategoryEvents()); 1095 proto.write(ZenPolicyProto.MESSAGES, getPriorityCategoryMessages()); 1096 proto.write(ZenPolicyProto.CALLS, getPriorityCategoryCalls()); 1097 proto.write(ZenPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers()); 1098 proto.write(ZenPolicyProto.ALARMS, getPriorityCategoryAlarms()); 1099 proto.write(ZenPolicyProto.MEDIA, getPriorityCategoryMedia()); 1100 proto.write(ZenPolicyProto.SYSTEM, getPriorityCategorySystem()); 1101 1102 proto.write(ZenPolicyProto.FULL_SCREEN_INTENT, getVisualEffectFullScreenIntent()); 1103 proto.write(ZenPolicyProto.LIGHTS, getVisualEffectLights()); 1104 proto.write(ZenPolicyProto.PEEK, getVisualEffectPeek()); 1105 proto.write(ZenPolicyProto.STATUS_BAR, getVisualEffectStatusBar()); 1106 proto.write(ZenPolicyProto.BADGE, getVisualEffectBadge()); 1107 proto.write(ZenPolicyProto.AMBIENT, getVisualEffectAmbient()); 1108 proto.write(ZenPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList()); 1109 1110 proto.write(ZenPolicyProto.PRIORITY_MESSAGES, getPriorityMessageSenders()); 1111 proto.write(ZenPolicyProto.PRIORITY_CALLS, getPriorityCallSenders()); 1112 proto.end(token); 1113 } 1114 1115 /** 1116 * Converts a policy to a statsd proto. 1117 * @hides 1118 */ toProto()1119 public byte[] toProto() { 1120 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 1121 ProtoOutputStream proto = new ProtoOutputStream(bytes); 1122 1123 proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls()); 1124 proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers()); 1125 proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages()); 1126 proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations()); 1127 proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders()); 1128 proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents()); 1129 proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms()); 1130 proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia()); 1131 proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem()); 1132 1133 proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent()); 1134 proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights()); 1135 proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek()); 1136 proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar()); 1137 proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge()); 1138 proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient()); 1139 proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList()); 1140 1141 proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders()); 1142 proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders()); 1143 proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders()); 1144 1145 proto.flush(); 1146 return bytes.toByteArray(); 1147 } 1148 1149 /** 1150 * Makes deep copy of this ZenPolicy. 1151 * @hide 1152 */ copy()1153 public @NonNull ZenPolicy copy() { 1154 final Parcel parcel = Parcel.obtain(); 1155 try { 1156 writeToParcel(parcel, 0); 1157 parcel.setDataPosition(0); 1158 return CREATOR.createFromParcel(parcel); 1159 } finally { 1160 parcel.recycle(); 1161 } 1162 } 1163 } 1164