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