diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-10-26 17:44:01 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-10-27 17:13:29 -0700 |
commit | e829fef63957a23b61cdb01bb692a17a041ff2dc (patch) | |
tree | 8e201bd9ef8648eb2d70f700205925f7b000d24f | |
parent | 162b689c5257d7e6a937cf734c0edde470c77969 (diff) | |
download | frameworks_base-e829fef63957a23b61cdb01bb692a17a041ff2dc.zip frameworks_base-e829fef63957a23b61cdb01bb692a17a041ff2dc.tar.gz frameworks_base-e829fef63957a23b61cdb01bb692a17a041ff2dc.tar.bz2 |
Add facility for broadcasts receives to do work asynchronously.
You can now call goAsync() and move your work to a background thread.
If you are that kind of receiver. You weirdo.
Also allows SharedPreferences.apply() to be committed off the main
thread after returning from onReceive().
Change-Id: I27f975910e28f230ababcaeb551eb9a78ec4fc76
-rw-r--r-- | api/current.xml | 155 | ||||
-rw-r--r-- | core/java/android/app/ActivityThread.java | 81 | ||||
-rw-r--r-- | core/java/android/app/LoadedApk.java | 95 | ||||
-rw-r--r-- | core/java/android/app/QueuedWork.java | 10 | ||||
-rw-r--r-- | core/java/android/content/BroadcastReceiver.java | 312 |
5 files changed, 507 insertions, 146 deletions
diff --git a/api/current.xml b/api/current.xml index 5ed07cf..841daa1 100644 --- a/api/current.xml +++ b/api/current.xml @@ -40186,6 +40186,17 @@ <parameter name="makeMap" type="boolean"> </parameter> </method> +<method name="goAsync" + return="android.content.BroadcastReceiver.PendingResult" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="isInitialStickyBroadcast" return="boolean" abstract="false" @@ -40321,6 +40332,150 @@ </parameter> </method> </class> +<class name="BroadcastReceiver.PendingResult" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="abortBroadcast" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="clearAbortBroadcast" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="finish" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getAbortBroadcast" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getResultCode" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getResultData" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getResultExtras" + return="android.os.Bundle" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="makeMap" type="boolean"> +</parameter> +</method> +<method name="setResult" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="code" type="int"> +</parameter> +<parameter name="data" type="java.lang.String"> +</parameter> +<parameter name="extras" type="android.os.Bundle"> +</parameter> +</method> +<method name="setResultCode" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="code" type="int"> +</parameter> +</method> +<method name="setResultData" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="data" type="java.lang.String"> +</parameter> +</method> +<method name="setResultExtras" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="extras" type="android.os.Bundle"> +</parameter> +</method> +</class> <class name="ClipData" extends="java.lang.Object" abstract="false" diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c5badaf..754295a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -117,12 +117,14 @@ final class RemoteServiceException extends AndroidRuntimeException { * {@hide} */ public final class ActivityThread { - static final String TAG = "ActivityThread"; + /** @hide */ + public static final String TAG = "ActivityThread"; private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565; private static final boolean DEBUG = false; static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; static final boolean DEBUG_MESSAGES = false; - static final boolean DEBUG_BROADCAST = false; + /** @hide */ + public static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; private static final boolean DEBUG_BACKUP = false; private static final boolean DEBUG_CONFIGURATION = false; @@ -262,18 +264,20 @@ public final class ActivityThread { } } - private static final class ReceiverData { + private static final class ReceiverData extends BroadcastReceiver.PendingResult { + public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras, + boolean ordered, boolean sticky, IBinder token) { + super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token); + this.intent = intent; + } + Intent intent; ActivityInfo info; - int resultCode; - String resultData; - Bundle resultExtras; - boolean sync; - boolean resultAbort; public String toString() { return "ReceiverData{intent=" + intent + " packageName=" + - info.packageName + " resultCode=" + resultCode - + " resultData=" + resultData + " resultExtras=" + resultExtras + "}"; + info.packageName + " resultCode=" + getResultCode() + + " resultData=" + getResultData() + " resultExtras=" + + getResultExtras(false) + "}"; } } @@ -466,15 +470,9 @@ public final class ActivityThread { public final void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode, String data, Bundle extras, boolean sync) { - ReceiverData r = new ReceiverData(); - - r.intent = intent; + ReceiverData r = new ReceiverData(intent, resultCode, data, extras, + sync, false, mAppThread.asBinder()); r.info = info; - r.resultCode = resultCode; - r.resultData = data; - r.resultExtras = extras; - r.sync = sync; - queueOrSendMessage(H.RECEIVER, r); } @@ -1799,18 +1797,12 @@ public final class ActivityThread { try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); data.intent.setExtrasClassLoader(cl); - if (data.resultExtras != null) { - data.resultExtras.setClassLoader(cl); - } + data.setExtrasClassLoader(cl); receiver = (BroadcastReceiver)cl.loadClass(component).newInstance(); } catch (Exception e) { - try { - if (DEBUG_BROADCAST) Slog.i(TAG, - "Finishing failed broadcast to " + data.intent.getComponent()); - mgr.finishReceiver(mAppThread.asBinder(), data.resultCode, - data.resultData, data.resultExtras, data.resultAbort); - } catch (RemoteException ex) { - } + if (DEBUG_BROADCAST) Slog.i(TAG, + "Finishing failed broadcast to " + data.intent.getComponent()); + data.sendFinished(mgr); throw new RuntimeException( "Unable to instantiate receiver " + component + ": " + e.toString(), e); @@ -1828,20 +1820,13 @@ public final class ActivityThread { + ", dir=" + packageInfo.getAppDir()); ContextImpl context = (ContextImpl)app.getBaseContext(); - receiver.setOrderedHint(true); - receiver.setResult(data.resultCode, data.resultData, - data.resultExtras); - receiver.setOrderedHint(data.sync); + receiver.setPendingResult(data); receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); } catch (Exception e) { - try { - if (DEBUG_BROADCAST) Slog.i(TAG, - "Finishing failed broadcast to " + data.intent.getComponent()); - mgr.finishReceiver(mAppThread.asBinder(), data.resultCode, - data.resultData, data.resultExtras, data.resultAbort); - } catch (RemoteException ex) { - } + if (DEBUG_BROADCAST) Slog.i(TAG, + "Finishing failed broadcast to " + data.intent.getComponent()); + data.sendFinished(mgr); if (!mInstrumentation.onException(receiver, e)) { throw new RuntimeException( "Unable to start receiver " + component @@ -1849,22 +1834,8 @@ public final class ActivityThread { } } - QueuedWork.waitToFinish(); - - try { - if (data.sync) { - if (DEBUG_BROADCAST) Slog.i(TAG, - "Finishing ordered broadcast to " + data.intent.getComponent()); - mgr.finishReceiver( - mAppThread.asBinder(), receiver.getResultCode(), - receiver.getResultData(), receiver.getResultExtras(false), - receiver.getAbortBroadcast()); - } else { - if (DEBUG_BROADCAST) Slog.i(TAG, - "Finishing broadcast to " + data.intent.getComponent()); - mgr.finishReceiver(mAppThread.asBinder(), 0, null, null, false); - } - } catch (RemoteException ex) { + if (receiver.getPendingResult() != null) { + data.finish(); } } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0644f96..7f24d27 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -660,37 +660,40 @@ final class LoadedApk { final IntentReceiverLeaked mLocation; RuntimeException mUnregisterLocation; - final class Args implements Runnable { + final class Args extends BroadcastReceiver.PendingResult implements Runnable { private Intent mCurIntent; - private int mCurCode; - private String mCurData; - private Bundle mCurMap; - private boolean mCurOrdered; - private boolean mCurSticky; - + private final boolean mOrdered; + + public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras, + boolean ordered, boolean sticky) { + super(resultCode, resultData, resultExtras, + mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, + ordered, sticky, mIIntentReceiver.asBinder()); + mCurIntent = intent; + mOrdered = ordered; + } + public void run() { - BroadcastReceiver receiver = mReceiver; + final BroadcastReceiver receiver = mReceiver; + final boolean ordered = mOrdered; + if (ActivityThread.DEBUG_BROADCAST) { int seq = mCurIntent.getIntExtra("seq", -1); Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction() + " seq=" + seq + " to " + mReceiver); Slog.i(ActivityThread.TAG, " mRegistered=" + mRegistered - + " mCurOrdered=" + mCurOrdered); + + " mOrderedHint=" + ordered); } - IActivityManager mgr = ActivityManagerNative.getDefault(); - Intent intent = mCurIntent; + final IActivityManager mgr = ActivityManagerNative.getDefault(); + final Intent intent = mCurIntent; mCurIntent = null; if (receiver == null) { - if (mRegistered && mCurOrdered) { - try { - if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, - "Finishing null broadcast to " + mReceiver); - mgr.finishReceiver(mIIntentReceiver, - mCurCode, mCurData, mCurMap, false); - } catch (RemoteException ex) { - } + if (mRegistered && ordered) { + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing null broadcast to " + mReceiver); + sendFinished(mgr); } return; } @@ -698,24 +701,14 @@ final class LoadedApk { try { ClassLoader cl = mReceiver.getClass().getClassLoader(); intent.setExtrasClassLoader(cl); - if (mCurMap != null) { - mCurMap.setClassLoader(cl); - } - receiver.setOrderedHint(true); - receiver.setResult(mCurCode, mCurData, mCurMap); - receiver.clearAbortBroadcast(); - receiver.setOrderedHint(mCurOrdered); - receiver.setInitialStickyHint(mCurSticky); + setExtrasClassLoader(cl); + receiver.setPendingResult(this); receiver.onReceive(mContext, intent); } catch (Exception e) { - if (mRegistered && mCurOrdered) { - try { - if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, - "Finishing failed broadcast to " + mReceiver); - mgr.finishReceiver(mIIntentReceiver, - mCurCode, mCurData, mCurMap, false); - } catch (RemoteException ex) { - } + if (mRegistered && ordered) { + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing failed broadcast to " + mReceiver); + sendFinished(mgr); } if (mInstrumentation == null || !mInstrumentation.onException(mReceiver, e)) { @@ -724,17 +717,9 @@ final class LoadedApk { + " in " + mReceiver, e); } } - if (mRegistered && mCurOrdered) { - try { - if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, - "Finishing broadcast to " + mReceiver); - mgr.finishReceiver(mIIntentReceiver, - receiver.getResultCode(), - receiver.getResultData(), - receiver.getResultExtras(false), - receiver.getAbortBroadcast()); - } catch (RemoteException ex) { - } + + if (receiver.getPendingResult() != null) { + finish(); } } } @@ -798,23 +783,13 @@ final class LoadedApk { Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq + " to " + mReceiver); } - Args args = new Args(); - args.mCurIntent = intent; - args.mCurCode = resultCode; - args.mCurData = data; - args.mCurMap = extras; - args.mCurOrdered = ordered; - args.mCurSticky = sticky; + Args args = new Args(intent, resultCode, data, extras, ordered, sticky); if (!mActivityThread.post(args)) { if (mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); - try { - if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, - "Finishing sync broadcast to " + mReceiver); - mgr.finishReceiver(mIIntentReceiver, args.mCurCode, - args.mCurData, args.mCurMap, false); - } catch (RemoteException ex) { - } + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing sync broadcast to " + mReceiver); + args.sendFinished(mgr); } } } diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java index af6bb1b..6ee4780 100644 --- a/core/java/android/app/QueuedWork.java +++ b/core/java/android/app/QueuedWork.java @@ -88,4 +88,14 @@ public class QueuedWork { toFinish.run(); } } + + /** + * Returns true if there is pending work to be done. Note that the + * result is out of data as soon as you receive it, so be careful how you + * use it. + */ + public static boolean hasPendingWork() { + return !sPendingWorkFinishers.isEmpty(); + } + } diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index b63d026..5939643 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -17,11 +17,14 @@ package android.content; import android.app.ActivityManagerNative; +import android.app.ActivityThread; import android.app.IActivityManager; +import android.app.QueuedWork; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.util.Slog; /** * Base class for code that will receive intents sent by sendBroadcast(). @@ -160,6 +163,226 @@ import android.util.Log; * the containing process active for the entire time of your operation. */ public abstract class BroadcastReceiver { + private PendingResult mPendingResult; + private boolean mDebugUnregister; + + /** + * State for a result that is pending for a broadcast receiver. Returned + * by {@link BroadcastReceiver#goAsync() goAsync()} + * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. + */ + public static class PendingResult { + /** @hide */ + public static final int TYPE_COMPONENT = 0; + /** @hide */ + public static final int TYPE_REGISTERED = 1; + /** @hide */ + public static final int TYPE_UNREGISTERED = 2; + + final int mType; + final boolean mOrderedHint; + final boolean mInitialStickyHint; + final IBinder mToken; + + int mResultCode; + String mResultData; + Bundle mResultExtras; + boolean mAbortBroadcast; + boolean mFinished; + + /** @hide */ + public PendingResult(int resultCode, String resultData, Bundle resultExtras, + int type, boolean ordered, boolean sticky, IBinder token) { + mResultCode = resultCode; + mResultData = resultData; + mResultExtras = resultExtras; + mType = type; + mOrderedHint = ordered; + mInitialStickyHint = sticky; + mToken = token; + } + + /** + * Version of {@link BroadcastReceiver#setResultCode(int) + * BroadcastReceiver.setResultCode(int)} for + * asynchronous broadcast handling. + */ + public final void setResultCode(int code) { + checkSynchronousHint(); + mResultCode = code; + } + + /** + * Version of {@link BroadcastReceiver#getResultCode() + * BroadcastReceiver.getResultCode()} for + * asynchronous broadcast handling. + */ + public final int getResultCode() { + return mResultCode; + } + + /** + * Version of {@link BroadcastReceiver#setResultData(String) + * BroadcastReceiver.setResultData(String)} for + * asynchronous broadcast handling. + */ + public final void setResultData(String data) { + checkSynchronousHint(); + mResultData = data; + } + + /** + * Version of {@link BroadcastReceiver#getResultData() + * BroadcastReceiver.getResultData()} for + * asynchronous broadcast handling. + */ + public final String getResultData() { + return mResultData; + } + + /** + * Version of {@link BroadcastReceiver#setResultExtras(Bundle) + * BroadcastReceiver.setResultExtras(Bundle)} for + * asynchronous broadcast handling. + */ + public final void setResultExtras(Bundle extras) { + checkSynchronousHint(); + mResultExtras = extras; + } + + /** + * Version of {@link BroadcastReceiver#getResultExtras(boolean) + * BroadcastReceiver.getResultExtras(boolean)} for + * asynchronous broadcast handling. + */ + public final Bundle getResultExtras(boolean makeMap) { + Bundle e = mResultExtras; + if (!makeMap) return e; + if (e == null) mResultExtras = e = new Bundle(); + return e; + } + + /** + * Version of {@link BroadcastReceiver#setResult(int, String, Bundle) + * BroadcastReceiver.setResult(int, String, Bundle)} for + * asynchronous broadcast handling. + */ + public final void setResult(int code, String data, Bundle extras) { + checkSynchronousHint(); + mResultCode = code; + mResultData = data; + mResultExtras = extras; + } + + /** + * Version of {@link BroadcastReceiver#getAbortBroadcast() + * BroadcastReceiver.getAbortBroadcast()} for + * asynchronous broadcast handling. + */ + public final boolean getAbortBroadcast() { + return mAbortBroadcast; + } + + /** + * Version of {@link BroadcastReceiver#abortBroadcast() + * BroadcastReceiver.abortBroadcast()} for + * asynchronous broadcast handling. + */ + public final void abortBroadcast() { + checkSynchronousHint(); + mAbortBroadcast = true; + } + + /** + * Version of {@link BroadcastReceiver#clearAbortBroadcast() + * BroadcastReceiver.clearAbortBroadcast()} for + * asynchronous broadcast handling. + */ + public final void clearAbortBroadcast() { + mAbortBroadcast = false; + } + + /** + * Finish the broadcast. The current result will be sent and the + * next broadcast will proceed. + */ + public final void finish() { + if (mType == TYPE_COMPONENT) { + final IActivityManager mgr = ActivityManagerNative.getDefault(); + if (QueuedWork.hasPendingWork()) { + // If this is a broadcast component, we need to make sure any + // queued work is complete before telling AM we are done, so + // we don't have our process killed before that. We now know + // there is pending work; put another piece of work at the end + // of the list to finish the broadcast, so we don't block this + // thread (which may be the main thread) to have it finished. + // + // Note that we don't need to use QueuedWork.add() with the + // runnable, since we know the AM is waiting for us until the + // executor gets to it. + QueuedWork.singleThreadExecutor().execute( new Runnable() { + @Override public void run() { + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing broadcast after work to component " + mToken); + sendFinished(mgr); + } + }); + } else { + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing broadcast to component " + mToken); + sendFinished(mgr); + } + } else if (mOrderedHint && mType != TYPE_UNREGISTERED) { + if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, + "Finishing broadcast to " + mToken); + final IActivityManager mgr = ActivityManagerNative.getDefault(); + sendFinished(mgr); + } + } + + /** @hide */ + public void setExtrasClassLoader(ClassLoader cl) { + if (mResultExtras != null) { + mResultExtras.setClassLoader(cl); + } + } + + /** @hide */ + public void sendFinished(IActivityManager am) { + synchronized (this) { + if (mFinished) { + throw new IllegalStateException("Broadcast already finished"); + } + mFinished = true; + + try { + if (mOrderedHint) { + am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras, + mAbortBroadcast); + } else { + // This broadcast was sent to a component; it is not ordered, + // but we still need to tell the activity manager we are done. + am.finishReceiver(mToken, 0, null, null, false); + } + } catch (RemoteException ex) { + } + } + } + + void checkSynchronousHint() { + // Note that we don't assert when receiving the initial sticky value, + // since that may have come from an ordered broadcast. We'll catch + // them later when the real broadcast happens again. + if (mOrderedHint || mInitialStickyHint) { + return; + } + RuntimeException e = new RuntimeException( + "BroadcastReceiver trying to return result during a non-ordered broadcast"); + e.fillInStackTrace(); + Log.e("BroadcastReceiver", e.getMessage(), e); + } + } + public BroadcastReceiver() { } @@ -197,6 +420,26 @@ public abstract class BroadcastReceiver { public abstract void onReceive(Context context, Intent intent); /** + * This can be called by an application in {@link #onReceive} to allow + * it to keep the broadcast active after returning from that function. + * This does <em>not</em> change the expectation of being relatively + * responsive to the broadcast (finishing it within 10s), but does allow + * the implementation to move work related to it over to another thread + * to avoid glitching the main UI thread due to disk IO. + * + * @return Returns a {@link PendingResult} representing the result of + * the active broadcast. The BroadcastRecord itself is no longer active; + * all data and other interaction must go through {@link PendingResult} + * APIs. The {@link PendingResult#finish PendingResult.finish()} method + * must be called once processing of the broadcast is done. + */ + public final PendingResult goAsync() { + PendingResult res = mPendingResult; + mPendingResult = null; + return res; + } + + /** * Provide a binder to an already-running service. This method is synchronous * and will not start the target service if it is not present, so it is safe * to call from {@link #onReceive}. @@ -225,9 +468,9 @@ public abstract class BroadcastReceiver { * {@link android.app.Activity#RESULT_OK} constants, though the * actual meaning of this value is ultimately up to the broadcaster. * - * <p><strong>This method does not work with non-ordered broadcasts such + * <p class="note">This method does not work with non-ordered broadcasts such * as those sent with {@link Context#sendBroadcast(Intent) - * Context.sendBroadcast}</strong></p> + * Context.sendBroadcast}</p> * * @param code The new result code. * @@ -235,7 +478,7 @@ public abstract class BroadcastReceiver { */ public final void setResultCode(int code) { checkSynchronousHint(); - mResultCode = code; + mPendingResult.mResultCode = code; } /** @@ -244,7 +487,7 @@ public abstract class BroadcastReceiver { * @return int The current result code. */ public final int getResultCode() { - return mResultCode; + return mPendingResult != null ? mPendingResult.mResultCode : 0; } /** @@ -264,7 +507,7 @@ public abstract class BroadcastReceiver { */ public final void setResultData(String data) { checkSynchronousHint(); - mResultData = data; + mPendingResult.mResultData = data; } /** @@ -274,7 +517,7 @@ public abstract class BroadcastReceiver { * @return String The current result data; may be null. */ public final String getResultData() { - return mResultData; + return mPendingResult != null ? mPendingResult.mResultData : null; } /** @@ -296,7 +539,7 @@ public abstract class BroadcastReceiver { */ public final void setResultExtras(Bundle extras) { checkSynchronousHint(); - mResultExtras = extras; + mPendingResult.mResultExtras = extras; } /** @@ -311,9 +554,12 @@ public abstract class BroadcastReceiver { * @return Map The current extras map. */ public final Bundle getResultExtras(boolean makeMap) { - Bundle e = mResultExtras; + if (mPendingResult == null) { + return null; + } + Bundle e = mPendingResult.mResultExtras; if (!makeMap) return e; - if (e == null) mResultExtras = e = new Bundle(); + if (e == null) mPendingResult.mResultExtras = e = new Bundle(); return e; } @@ -341,9 +587,9 @@ public abstract class BroadcastReceiver { */ public final void setResult(int code, String data, Bundle extras) { checkSynchronousHint(); - mResultCode = code; - mResultData = data; - mResultExtras = extras; + mPendingResult.mResultCode = code; + mPendingResult.mResultData = data; + mPendingResult.mResultExtras = extras; } /** @@ -353,7 +599,7 @@ public abstract class BroadcastReceiver { * @return True if the broadcast should be aborted. */ public final boolean getAbortBroadcast() { - return mAbortBroadcast; + return mPendingResult != null ? mPendingResult.mAbortBroadcast : false; } /** @@ -372,7 +618,7 @@ public abstract class BroadcastReceiver { */ public final void abortBroadcast() { checkSynchronousHint(); - mAbortBroadcast = true; + mPendingResult.mAbortBroadcast = true; } /** @@ -380,7 +626,9 @@ public abstract class BroadcastReceiver { * broadcast. */ public final void clearAbortBroadcast() { - mAbortBroadcast = false; + if (mPendingResult != null) { + mPendingResult.mAbortBroadcast = false; + } } /** @@ -388,7 +636,7 @@ public abstract class BroadcastReceiver { * broadcast. */ public final boolean isOrderedBroadcast() { - return mOrderedHint; + return mPendingResult != null ? mPendingResult.mOrderedHint : false; } /** @@ -398,7 +646,7 @@ public abstract class BroadcastReceiver { * not directly the result of a broadcast right now. */ public final boolean isInitialStickyBroadcast() { - return mInitialStickyHint; + return mPendingResult != null ? mPendingResult.mInitialStickyHint : false; } /** @@ -406,15 +654,21 @@ public abstract class BroadcastReceiver { * running in ordered mode. */ public final void setOrderedHint(boolean isOrdered) { - mOrderedHint = isOrdered; + // Accidentally left in the SDK. } /** - * For internal use, sets the hint about whether this BroadcastReceiver is - * receiving the initial sticky broadcast value. @hide + * For internal use to set the result data that is active. @hide */ - public final void setInitialStickyHint(boolean isInitialSticky) { - mInitialStickyHint = isInitialSticky; + public final void setPendingResult(PendingResult result) { + mPendingResult = result; + } + + /** + * For internal use to set the result data that is active. @hide + */ + public final PendingResult getPendingResult() { + return mPendingResult; } /** @@ -440,10 +694,14 @@ public abstract class BroadcastReceiver { } void checkSynchronousHint() { + if (mPendingResult == null) { + throw new IllegalStateException("Call while result is not pending"); + } + // Note that we don't assert when receiving the initial sticky value, // since that may have come from an ordered broadcast. We'll catch // them later when the real broadcast happens again. - if (mOrderedHint || mInitialStickyHint) { + if (mPendingResult.mOrderedHint || mPendingResult.mInitialStickyHint) { return; } RuntimeException e = new RuntimeException( @@ -451,13 +709,5 @@ public abstract class BroadcastReceiver { e.fillInStackTrace(); Log.e("BroadcastReceiver", e.getMessage(), e); } - - private int mResultCode; - private String mResultData; - private Bundle mResultExtras; - private boolean mAbortBroadcast; - private boolean mDebugUnregister; - private boolean mOrderedHint; - private boolean mInitialStickyHint; } |