1 /* 2 * Copyright (C) 2006 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.content; 18 19 import android.annotation.UnsupportedAppUsage; 20 import android.app.ActivityManager; 21 import android.app.ActivityThread; 22 import android.app.IActivityManager; 23 import android.app.QueuedWork; 24 import android.os.Build; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.os.RemoteException; 28 import android.util.Log; 29 import android.util.Slog; 30 31 /** 32 * Base class for code that receives and handles broadcast intents sent by 33 * {@link android.content.Context#sendBroadcast(Intent)}. 34 * 35 * <p>You can either dynamically register an instance of this class with 36 * {@link Context#registerReceiver Context.registerReceiver()} 37 * or statically declare an implementation with the 38 * {@link android.R.styleable#AndroidManifestReceiver <receiver>} 39 * tag in your <code>AndroidManifest.xml</code>. 40 * 41 * <div class="special reference"> 42 * <h3>Developer Guides</h3> 43 * <p>For more information about using BroadcastReceiver, read the 44 * <a href="{@docRoot}guide/components/broadcasts.html">Broadcasts</a> developer guide.</p></div> 45 * 46 */ 47 public abstract class BroadcastReceiver { 48 @UnsupportedAppUsage 49 private PendingResult mPendingResult; 50 private boolean mDebugUnregister; 51 52 /** 53 * State for a result that is pending for a broadcast receiver. Returned 54 * by {@link BroadcastReceiver#goAsync() goAsync()} 55 * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. 56 * This allows you to return from onReceive() without having the broadcast 57 * terminate; you must call {@link #finish()} once you are done with the 58 * broadcast. This allows you to process the broadcast off of the main 59 * thread of your app. 60 * 61 * <p>Note on threading: the state inside of this class is not itself 62 * thread-safe, however you can use it from any thread if you properly 63 * sure that you do not have races. Typically this means you will hand 64 * the entire object to another thread, which will be solely responsible 65 * for setting any results and finally calling {@link #finish()}. 66 */ 67 public static class PendingResult { 68 /** @hide */ 69 public static final int TYPE_COMPONENT = 0; 70 /** @hide */ 71 public static final int TYPE_REGISTERED = 1; 72 /** @hide */ 73 public static final int TYPE_UNREGISTERED = 2; 74 75 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 76 final int mType; 77 @UnsupportedAppUsage 78 final boolean mOrderedHint; 79 @UnsupportedAppUsage 80 final boolean mInitialStickyHint; 81 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 82 final IBinder mToken; 83 @UnsupportedAppUsage 84 final int mSendingUser; 85 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 86 final int mFlags; 87 88 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 89 int mResultCode; 90 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 91 String mResultData; 92 @UnsupportedAppUsage 93 Bundle mResultExtras; 94 @UnsupportedAppUsage 95 boolean mAbortBroadcast; 96 @UnsupportedAppUsage 97 boolean mFinished; 98 99 /** @hide */ 100 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) PendingResult(int resultCode, String resultData, Bundle resultExtras, int type, boolean ordered, boolean sticky, IBinder token, int userId, int flags)101 public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type, 102 boolean ordered, boolean sticky, IBinder token, int userId, int flags) { 103 mResultCode = resultCode; 104 mResultData = resultData; 105 mResultExtras = resultExtras; 106 mType = type; 107 mOrderedHint = ordered; 108 mInitialStickyHint = sticky; 109 mToken = token; 110 mSendingUser = userId; 111 mFlags = flags; 112 } 113 114 /** 115 * Version of {@link BroadcastReceiver#setResultCode(int) 116 * BroadcastReceiver.setResultCode(int)} for 117 * asynchronous broadcast handling. 118 */ setResultCode(int code)119 public final void setResultCode(int code) { 120 checkSynchronousHint(); 121 mResultCode = code; 122 } 123 124 /** 125 * Version of {@link BroadcastReceiver#getResultCode() 126 * BroadcastReceiver.getResultCode()} for 127 * asynchronous broadcast handling. 128 */ getResultCode()129 public final int getResultCode() { 130 return mResultCode; 131 } 132 133 /** 134 * Version of {@link BroadcastReceiver#setResultData(String) 135 * BroadcastReceiver.setResultData(String)} for 136 * asynchronous broadcast handling. 137 */ setResultData(String data)138 public final void setResultData(String data) { 139 checkSynchronousHint(); 140 mResultData = data; 141 } 142 143 /** 144 * Version of {@link BroadcastReceiver#getResultData() 145 * BroadcastReceiver.getResultData()} for 146 * asynchronous broadcast handling. 147 */ getResultData()148 public final String getResultData() { 149 return mResultData; 150 } 151 152 /** 153 * Version of {@link BroadcastReceiver#setResultExtras(Bundle) 154 * BroadcastReceiver.setResultExtras(Bundle)} for 155 * asynchronous broadcast handling. 156 */ setResultExtras(Bundle extras)157 public final void setResultExtras(Bundle extras) { 158 checkSynchronousHint(); 159 mResultExtras = extras; 160 } 161 162 /** 163 * Version of {@link BroadcastReceiver#getResultExtras(boolean) 164 * BroadcastReceiver.getResultExtras(boolean)} for 165 * asynchronous broadcast handling. 166 */ getResultExtras(boolean makeMap)167 public final Bundle getResultExtras(boolean makeMap) { 168 Bundle e = mResultExtras; 169 if (!makeMap) return e; 170 if (e == null) mResultExtras = e = new Bundle(); 171 return e; 172 } 173 174 /** 175 * Version of {@link BroadcastReceiver#setResult(int, String, Bundle) 176 * BroadcastReceiver.setResult(int, String, Bundle)} for 177 * asynchronous broadcast handling. 178 */ setResult(int code, String data, Bundle extras)179 public final void setResult(int code, String data, Bundle extras) { 180 checkSynchronousHint(); 181 mResultCode = code; 182 mResultData = data; 183 mResultExtras = extras; 184 } 185 186 /** 187 * Version of {@link BroadcastReceiver#getAbortBroadcast() 188 * BroadcastReceiver.getAbortBroadcast()} for 189 * asynchronous broadcast handling. 190 */ getAbortBroadcast()191 public final boolean getAbortBroadcast() { 192 return mAbortBroadcast; 193 } 194 195 /** 196 * Version of {@link BroadcastReceiver#abortBroadcast() 197 * BroadcastReceiver.abortBroadcast()} for 198 * asynchronous broadcast handling. 199 */ abortBroadcast()200 public final void abortBroadcast() { 201 checkSynchronousHint(); 202 mAbortBroadcast = true; 203 } 204 205 /** 206 * Version of {@link BroadcastReceiver#clearAbortBroadcast() 207 * BroadcastReceiver.clearAbortBroadcast()} for 208 * asynchronous broadcast handling. 209 */ clearAbortBroadcast()210 public final void clearAbortBroadcast() { 211 mAbortBroadcast = false; 212 } 213 214 /** 215 * Finish the broadcast. The current result will be sent and the 216 * next broadcast will proceed. 217 */ finish()218 public final void finish() { 219 if (mType == TYPE_COMPONENT) { 220 final IActivityManager mgr = ActivityManager.getService(); 221 if (QueuedWork.hasPendingWork()) { 222 // If this is a broadcast component, we need to make sure any 223 // queued work is complete before telling AM we are done, so 224 // we don't have our process killed before that. We now know 225 // there is pending work; put another piece of work at the end 226 // of the list to finish the broadcast, so we don't block this 227 // thread (which may be the main thread) to have it finished. 228 // 229 // Note that we don't need to use QueuedWork.addFinisher() with the 230 // runnable, since we know the AM is waiting for us until the 231 // executor gets to it. 232 QueuedWork.queue(new Runnable() { 233 @Override public void run() { 234 if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, 235 "Finishing broadcast after work to component " + mToken); 236 sendFinished(mgr); 237 } 238 }, false); 239 } else { 240 if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, 241 "Finishing broadcast to component " + mToken); 242 sendFinished(mgr); 243 } 244 } else if (mOrderedHint && mType != TYPE_UNREGISTERED) { 245 if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, 246 "Finishing broadcast to " + mToken); 247 final IActivityManager mgr = ActivityManager.getService(); 248 sendFinished(mgr); 249 } 250 } 251 252 /** @hide */ setExtrasClassLoader(ClassLoader cl)253 public void setExtrasClassLoader(ClassLoader cl) { 254 if (mResultExtras != null) { 255 mResultExtras.setClassLoader(cl); 256 } 257 } 258 259 /** @hide */ sendFinished(IActivityManager am)260 public void sendFinished(IActivityManager am) { 261 synchronized (this) { 262 if (mFinished) { 263 throw new IllegalStateException("Broadcast already finished"); 264 } 265 mFinished = true; 266 267 try { 268 if (mResultExtras != null) { 269 mResultExtras.setAllowFds(false); 270 } 271 if (mOrderedHint) { 272 am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras, 273 mAbortBroadcast, mFlags); 274 } else { 275 // This broadcast was sent to a component; it is not ordered, 276 // but we still need to tell the activity manager we are done. 277 am.finishReceiver(mToken, 0, null, null, false, mFlags); 278 } 279 } catch (RemoteException ex) { 280 } 281 } 282 } 283 284 /** @hide */ getSendingUserId()285 public int getSendingUserId() { 286 return mSendingUser; 287 } 288 checkSynchronousHint()289 void checkSynchronousHint() { 290 // Note that we don't assert when receiving the initial sticky value, 291 // since that may have come from an ordered broadcast. We'll catch 292 // them later when the real broadcast happens again. 293 if (mOrderedHint || mInitialStickyHint) { 294 return; 295 } 296 RuntimeException e = new RuntimeException( 297 "BroadcastReceiver trying to return result during a non-ordered broadcast"); 298 e.fillInStackTrace(); 299 Log.e("BroadcastReceiver", e.getMessage(), e); 300 } 301 } 302 BroadcastReceiver()303 public BroadcastReceiver() { 304 } 305 306 /** 307 * This method is called when the BroadcastReceiver is receiving an Intent 308 * broadcast. During this time you can use the other methods on 309 * BroadcastReceiver to view/modify the current result values. This method 310 * is always called within the main thread of its process, unless you 311 * explicitly asked for it to be scheduled on a different thread using 312 * {@link android.content.Context#registerReceiver(BroadcastReceiver, 313 * IntentFilter, String, android.os.Handler)}. When it runs on the main 314 * thread you should 315 * never perform long-running operations in it (there is a timeout of 316 * 10 seconds that the system allows before considering the receiver to 317 * be blocked and a candidate to be killed). You cannot launch a popup dialog 318 * in your implementation of onReceive(). 319 * 320 * <p><b>If this BroadcastReceiver was launched through a <receiver> tag, 321 * then the object is no longer alive after returning from this 322 * function.</b> This means you should not perform any operations that 323 * return a result to you asynchronously. If you need to perform any follow up 324 * background work, schedule a {@link android.app.job.JobService} with 325 * {@link android.app.job.JobScheduler}. 326 * 327 * If you wish to interact with a service that is already running and previously 328 * bound using {@link android.content.Context#bindService(Intent, ServiceConnection, int) bindService()}, 329 * you can use {@link #peekService}. 330 * 331 * <p>The Intent filters used in {@link android.content.Context#registerReceiver} 332 * and in application manifests are <em>not</em> guaranteed to be exclusive. They 333 * are hints to the operating system about how to find suitable recipients. It is 334 * possible for senders to force delivery to specific recipients, bypassing filter 335 * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} 336 * implementations should respond only to known actions, ignoring any unexpected 337 * Intents that they may receive. 338 * 339 * @param context The Context in which the receiver is running. 340 * @param intent The Intent being received. 341 */ onReceive(Context context, Intent intent)342 public abstract void onReceive(Context context, Intent intent); 343 344 /** 345 * This can be called by an application in {@link #onReceive} to allow 346 * it to keep the broadcast active after returning from that function. 347 * This does <em>not</em> change the expectation of being relatively 348 * responsive to the broadcast, but does allow 349 * the implementation to move work related to it over to another thread 350 * to avoid glitching the main UI thread due to disk IO. 351 * 352 * <p>As a general rule, broadcast receivers are allowed to run for up to 10 seconds 353 * before they system will consider them non-responsive and ANR the app. Since these usually 354 * execute on the app's main thread, they are already bound by the ~5 second time limit 355 * of various operations that can happen there (not to mention just avoiding UI jank), so 356 * the receive limit is generally not of concern. However, once you use {@code goAsync}, though 357 * able to be off the main thread, the broadcast execution limit still applies, and that 358 * includes the time spent between calling this method and ultimately 359 * {@link PendingResult#finish() PendingResult.finish()}.</p> 360 * 361 * <p>If you are taking advantage of this method to have more time to execute, it is useful 362 * to know that the available time can be longer in certain situations. In particular, if 363 * the broadcast you are receiving is not a foreground broadcast (that is, the sender has not 364 * used {@link Intent#FLAG_RECEIVER_FOREGROUND}), then more time is allowed for the receivers 365 * to run, allowing them to execute for 30 seconds or even a bit more. This is something that 366 * receivers should rarely take advantage of (long work should be punted to another system 367 * facility such as {@link android.app.job.JobScheduler}, {@link android.app.Service}, or 368 * see especially {@link android.support.v4.app.JobIntentService}), but can be useful in 369 * certain rare cases where it is necessary to do some work as soon as the broadcast is 370 * delivered. Keep in mind that the work you do here will block further broadcasts until 371 * it completes, so taking advantage of this at all excessively can be counter-productive 372 * and cause later events to be received more slowly.</p> 373 * 374 * @return Returns a {@link PendingResult} representing the result of 375 * the active broadcast. The BroadcastRecord itself is no longer active; 376 * all data and other interaction must go through {@link PendingResult} 377 * APIs. The {@link PendingResult#finish PendingResult.finish()} method 378 * must be called once processing of the broadcast is done. 379 */ goAsync()380 public final PendingResult goAsync() { 381 PendingResult res = mPendingResult; 382 mPendingResult = null; 383 return res; 384 } 385 386 /** 387 * Provide a binder to an already-bound service. This method is synchronous 388 * and will not start the target service if it is not present, so it is safe 389 * to call from {@link #onReceive}. 390 * 391 * For peekService() to return a non null {@link android.os.IBinder} interface 392 * the service must have published it before. In other words some component 393 * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it. 394 * 395 * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)} 396 * @param service Identifies the already-bound service you wish to use. See 397 * {@link android.content.Context#bindService(Intent, ServiceConnection, int)} 398 * for more information. 399 */ peekService(Context myContext, Intent service)400 public IBinder peekService(Context myContext, Intent service) { 401 IActivityManager am = ActivityManager.getService(); 402 IBinder binder = null; 403 try { 404 service.prepareToLeaveProcess(myContext); 405 binder = am.peekService(service, service.resolveTypeIfNeeded( 406 myContext.getContentResolver()), myContext.getOpPackageName()); 407 } catch (RemoteException e) { 408 } 409 return binder; 410 } 411 412 /** 413 * Change the current result code of this broadcast; only works with 414 * broadcasts sent through 415 * {@link Context#sendOrderedBroadcast(Intent, String) 416 * Context.sendOrderedBroadcast}. Often uses the 417 * Activity {@link android.app.Activity#RESULT_CANCELED} and 418 * {@link android.app.Activity#RESULT_OK} constants, though the 419 * actual meaning of this value is ultimately up to the broadcaster. 420 * 421 * <p class="note">This method does not work with non-ordered broadcasts such 422 * as those sent with {@link Context#sendBroadcast(Intent) 423 * Context.sendBroadcast}</p> 424 * 425 * @param code The new result code. 426 * 427 * @see #setResult(int, String, Bundle) 428 */ setResultCode(int code)429 public final void setResultCode(int code) { 430 checkSynchronousHint(); 431 mPendingResult.mResultCode = code; 432 } 433 434 /** 435 * Retrieve the current result code, as set by the previous receiver. 436 * 437 * @return int The current result code. 438 */ getResultCode()439 public final int getResultCode() { 440 return mPendingResult != null ? mPendingResult.mResultCode : 0; 441 } 442 443 /** 444 * Change the current result data of this broadcast; only works with 445 * broadcasts sent through 446 * {@link Context#sendOrderedBroadcast(Intent, String) 447 * Context.sendOrderedBroadcast}. This is an arbitrary 448 * string whose interpretation is up to the broadcaster. 449 * 450 * <p><strong>This method does not work with non-ordered broadcasts such 451 * as those sent with {@link Context#sendBroadcast(Intent) 452 * Context.sendBroadcast}</strong></p> 453 * 454 * @param data The new result data; may be null. 455 * 456 * @see #setResult(int, String, Bundle) 457 */ setResultData(String data)458 public final void setResultData(String data) { 459 checkSynchronousHint(); 460 mPendingResult.mResultData = data; 461 } 462 463 /** 464 * Retrieve the current result data, as set by the previous receiver. 465 * Often this is null. 466 * 467 * @return String The current result data; may be null. 468 */ getResultData()469 public final String getResultData() { 470 return mPendingResult != null ? mPendingResult.mResultData : null; 471 } 472 473 /** 474 * Change the current result extras of this broadcast; only works with 475 * broadcasts sent through 476 * {@link Context#sendOrderedBroadcast(Intent, String) 477 * Context.sendOrderedBroadcast}. This is a Bundle 478 * holding arbitrary data, whose interpretation is up to the 479 * broadcaster. Can be set to null. Calling this method completely 480 * replaces the current map (if any). 481 * 482 * <p><strong>This method does not work with non-ordered broadcasts such 483 * as those sent with {@link Context#sendBroadcast(Intent) 484 * Context.sendBroadcast}</strong></p> 485 * 486 * @param extras The new extra data map; may be null. 487 * 488 * @see #setResult(int, String, Bundle) 489 */ setResultExtras(Bundle extras)490 public final void setResultExtras(Bundle extras) { 491 checkSynchronousHint(); 492 mPendingResult.mResultExtras = extras; 493 } 494 495 /** 496 * Retrieve the current result extra data, as set by the previous receiver. 497 * Any changes you make to the returned Map will be propagated to the next 498 * receiver. 499 * 500 * @param makeMap If true then a new empty Map will be made for you if the 501 * current Map is null; if false you should be prepared to 502 * receive a null Map. 503 * 504 * @return Map The current extras map. 505 */ getResultExtras(boolean makeMap)506 public final Bundle getResultExtras(boolean makeMap) { 507 if (mPendingResult == null) { 508 return null; 509 } 510 Bundle e = mPendingResult.mResultExtras; 511 if (!makeMap) return e; 512 if (e == null) mPendingResult.mResultExtras = e = new Bundle(); 513 return e; 514 } 515 516 /** 517 * Change all of the result data returned from this broadcasts; only works 518 * with broadcasts sent through 519 * {@link Context#sendOrderedBroadcast(Intent, String) 520 * Context.sendOrderedBroadcast}. All current result data is replaced 521 * by the value given to this method. 522 * 523 * <p><strong>This method does not work with non-ordered broadcasts such 524 * as those sent with {@link Context#sendBroadcast(Intent) 525 * Context.sendBroadcast}</strong></p> 526 * 527 * @param code The new result code. Often uses the 528 * Activity {@link android.app.Activity#RESULT_CANCELED} and 529 * {@link android.app.Activity#RESULT_OK} constants, though the 530 * actual meaning of this value is ultimately up to the broadcaster. 531 * @param data The new result data. This is an arbitrary 532 * string whose interpretation is up to the broadcaster; may be null. 533 * @param extras The new extra data map. This is a Bundle 534 * holding arbitrary data, whose interpretation is up to the 535 * broadcaster. Can be set to null. This completely 536 * replaces the current map (if any). 537 */ setResult(int code, String data, Bundle extras)538 public final void setResult(int code, String data, Bundle extras) { 539 checkSynchronousHint(); 540 mPendingResult.mResultCode = code; 541 mPendingResult.mResultData = data; 542 mPendingResult.mResultExtras = extras; 543 } 544 545 /** 546 * Returns the flag indicating whether or not this receiver should 547 * abort the current broadcast. 548 * 549 * @return True if the broadcast should be aborted. 550 */ getAbortBroadcast()551 public final boolean getAbortBroadcast() { 552 return mPendingResult != null ? mPendingResult.mAbortBroadcast : false; 553 } 554 555 /** 556 * Sets the flag indicating that this receiver should abort the 557 * current broadcast; only works with broadcasts sent through 558 * {@link Context#sendOrderedBroadcast(Intent, String) 559 * Context.sendOrderedBroadcast}. This will prevent 560 * any other broadcast receivers from receiving the broadcast. It will still 561 * call {@link #onReceive} of the BroadcastReceiver that the caller of 562 * {@link Context#sendOrderedBroadcast(Intent, String) 563 * Context.sendOrderedBroadcast} passed in. 564 * 565 * <p><strong>This method does not work with non-ordered broadcasts such 566 * as those sent with {@link Context#sendBroadcast(Intent) 567 * Context.sendBroadcast}</strong></p> 568 */ abortBroadcast()569 public final void abortBroadcast() { 570 checkSynchronousHint(); 571 mPendingResult.mAbortBroadcast = true; 572 } 573 574 /** 575 * Clears the flag indicating that this receiver should abort the current 576 * broadcast. 577 */ clearAbortBroadcast()578 public final void clearAbortBroadcast() { 579 if (mPendingResult != null) { 580 mPendingResult.mAbortBroadcast = false; 581 } 582 } 583 584 /** 585 * Returns true if the receiver is currently processing an ordered 586 * broadcast. 587 */ isOrderedBroadcast()588 public final boolean isOrderedBroadcast() { 589 return mPendingResult != null ? mPendingResult.mOrderedHint : false; 590 } 591 592 /** 593 * Returns true if the receiver is currently processing the initial 594 * value of a sticky broadcast -- that is, the value that was last 595 * broadcast and is currently held in the sticky cache, so this is 596 * not directly the result of a broadcast right now. 597 */ isInitialStickyBroadcast()598 public final boolean isInitialStickyBroadcast() { 599 return mPendingResult != null ? mPendingResult.mInitialStickyHint : false; 600 } 601 602 /** 603 * For internal use, sets the hint about whether this BroadcastReceiver is 604 * running in ordered mode. 605 */ setOrderedHint(boolean isOrdered)606 public final void setOrderedHint(boolean isOrdered) { 607 // Accidentally left in the SDK. 608 } 609 610 /** 611 * For internal use to set the result data that is active. @hide 612 */ 613 @UnsupportedAppUsage setPendingResult(PendingResult result)614 public final void setPendingResult(PendingResult result) { 615 mPendingResult = result; 616 } 617 618 /** 619 * For internal use to set the result data that is active. @hide 620 */ 621 @UnsupportedAppUsage getPendingResult()622 public final PendingResult getPendingResult() { 623 return mPendingResult; 624 } 625 626 /** @hide */ getSendingUserId()627 public int getSendingUserId() { 628 return mPendingResult.mSendingUser; 629 } 630 631 /** 632 * Control inclusion of debugging help for mismatched 633 * calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) 634 * Context.registerReceiver()}. 635 * If called with true, before given to registerReceiver(), then the 636 * callstack of the following {@link Context#unregisterReceiver(BroadcastReceiver) 637 * Context.unregisterReceiver()} call is retained, to be printed if a later 638 * incorrect unregister call is made. Note that doing this requires retaining 639 * information about the BroadcastReceiver for the lifetime of the app, 640 * resulting in a leak -- this should only be used for debugging. 641 */ setDebugUnregister(boolean debug)642 public final void setDebugUnregister(boolean debug) { 643 mDebugUnregister = debug; 644 } 645 646 /** 647 * Return the last value given to {@link #setDebugUnregister}. 648 */ getDebugUnregister()649 public final boolean getDebugUnregister() { 650 return mDebugUnregister; 651 } 652 checkSynchronousHint()653 void checkSynchronousHint() { 654 if (mPendingResult == null) { 655 throw new IllegalStateException("Call while result is not pending"); 656 } 657 658 // Note that we don't assert when receiving the initial sticky value, 659 // since that may have come from an ordered broadcast. We'll catch 660 // them later when the real broadcast happens again. 661 if (mPendingResult.mOrderedHint || mPendingResult.mInitialStickyHint) { 662 return; 663 } 664 RuntimeException e = new RuntimeException( 665 "BroadcastReceiver trying to return result during a non-ordered broadcast"); 666 e.fillInStackTrace(); 667 Log.e("BroadcastReceiver", e.getMessage(), e); 668 } 669 } 670 671