More cleanup of Loader APIs.

- Remove old method names.
- Introduce onXxx() hooks to Loader.
- Improve debugging.

Change-Id: I3fba072a05c7023aa7d2c3eb4e126feb514ab6d8
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 348149e..f06f2cf 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -27,6 +27,7 @@
 import android.os.Parcelable;
 import android.util.AndroidRuntimeException;
 import android.util.AttributeSet;
+import android.util.DebugUtils;
 import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.LayoutInflater;
@@ -526,17 +527,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
-        String simpleName = getClass().getSimpleName();
-        if (simpleName == null || simpleName.isEmpty()) {
-            simpleName = getClass().getName();
-            int end = simpleName.lastIndexOf('.');
-            if (end > 0) {
-                simpleName = simpleName.substring(end+1);
-            }
-        }
-        sb.append(simpleName);
-        sb.append("{");
-        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        DebugUtils.buildShortClassTag(this, sb);
         if (mIndex >= 0) {
             sb.append(" #");
             sb.append(mIndex);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 488b673..fe2ebed 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Menu;
@@ -251,6 +252,15 @@
      * @param args Additional arguments to the dump request.
      */
     public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
+
+    /**
+     * Control whether the framework's internal fragment manager debugging
+     * logs are turned on.  If enabled, you will see output in logcat as
+     * the framework performs fragment operations.
+     */
+    public static void enableDebugLogging(boolean enabled) {
+        FragmentManagerImpl.DEBUG = enabled;
+    }
 }
 
 final class FragmentManagerState implements Parcelable {
@@ -293,7 +303,7 @@
  * Container for fragments associated with an activity.
  */
 final class FragmentManagerImpl extends FragmentManager {
-    static final boolean DEBUG = false;
+    static boolean DEBUG = true;
     static final String TAG = "FragmentManager";
     
     static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
@@ -451,6 +461,17 @@
     }
 
     @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("FragmentManager{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" in ");
+        DebugUtils.buildShortClassTag(mActivity, sb);
+        sb.append("}}");
+        return sb.toString();
+    }
+
+    @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         if (mActive == null || mActive.size() <= 0) {
             return;
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 7125054..0e97abb 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -18,6 +18,7 @@
 
 import android.content.Loader;
 import android.os.Bundle;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -122,6 +123,8 @@
      * @param id A unique identifier for this loader.  Can be whatever you want.
      * Identifiers are scoped to a particular LoaderManager instance.
      * @param args Optional arguments to supply to the loader at construction.
+     * If a loader already exists (a new one does not need to be created), this
+     * parameter will be ignored and the last arguments continue to be used.
      * @param callback Interface the LoaderManager will call to report about
      * changes in the state of the loader.  Required.
      */
@@ -177,11 +180,20 @@
      * @param args Additional arguments to the dump request.
      */
     public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
+
+    /**
+     * Control whether the framework's internal loader manager debugging
+     * logs are turned on.  If enabled, you will see output in logcat as
+     * the framework performs loader operations.
+     */
+    public static void enableDebugLogging(boolean enabled) {
+        LoaderManagerImpl.DEBUG = enabled;
+    }
 }
 
 class LoaderManagerImpl extends LoaderManager {
-    static final String TAG = "LoaderManagerImpl";
-    static final boolean DEBUG = true;
+    static final String TAG = "LoaderManager";
+    static boolean DEBUG = true;
 
     // These are the currently active loaders.  A loader is here
     // from the time its load is started until it has been explicitly
@@ -237,6 +249,11 @@
                mLoader = mCallbacks.onCreateLoader(mId, mArgs);
             }
             if (mLoader != null) {
+                if (mLoader.getClass().isMemberClass()) {
+                    throw new IllegalArgumentException(
+                            "Object returned from onCreateLoader must not be a member class: "
+                            + mLoader);
+                }
                 if (!mListenerRegistered) {
                     mLoader.registerListener(mId, this);
                     mListenerRegistered = true;
@@ -295,7 +312,10 @@
         void destroy() {
             if (DEBUG) Log.v(TAG, "  Destroying: " + this);
             mDestroyed = true;
-            if (mCallbacks != null && mLoader != null && mData != null && mNeedReset) {
+            boolean needReset = mNeedReset;
+            mNeedReset = false;
+            if (mCallbacks != null && mLoader != null && mData != null && needReset) {
+                if (DEBUG) Log.v(TAG, "  Reseting: " + this);
                 String lastBecause = null;
                 if (mActivity != null) {
                     lastBecause = mActivity.mFragments.mNoTransactionsBecause;
@@ -309,7 +329,6 @@
                     }
                 }
             }
-            mNeedReset = false;
             mCallbacks = null;
             mData = null;
             if (mLoader != null) {
@@ -322,9 +341,10 @@
         }
         
         @Override public void onLoadComplete(Loader<Object> loader, Object data) {
-            if (DEBUG) Log.v(TAG, "onLoadComplete: " + this + " mDestroyed=" + mDestroyed);
+            if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);
 
             if (mDestroyed) {
+                if (DEBUG) Log.v(TAG, "  Ignoring load complete -- destroyed");
                 return;
             }
             
@@ -335,7 +355,7 @@
                 callOnLoadFinished(loader, data);
             }
 
-            if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
+            //if (DEBUG) Log.v(TAG, "  onLoadFinished returned: " + this);
 
             // We have now given the application the new loader with its
             // loaded data, so it should have stopped using the previous
@@ -357,6 +377,8 @@
                     mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
                 }
                 try {
+                    if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
+                            + loader.dataToString(data));
                     mCallbacks.onLoadFinished(loader, data);
                 } finally {
                     if (mActivity != null) {
@@ -374,21 +396,9 @@
             sb.append(Integer.toHexString(System.identityHashCode(this)));
             sb.append(" #");
             sb.append(mId);
-            if (mArgs != null) {
-                sb.append(" ");
-                sb.append(mArgs.toString());
-            }
-            sb.append("}");
-            return sb.toString();
-        }
-
-        public String toBasicString() {
-            StringBuilder sb = new StringBuilder(64);
-            sb.append("LoaderInfo{");
-            sb.append(Integer.toHexString(System.identityHashCode(this)));
-            sb.append(" #");
-            sb.append(mId);
-            sb.append("}");
+            sb.append(" : ");
+            DebugUtils.buildShortClassTag(mLoader, sb);
+            sb.append("}}");
             return sb.toString();
         }
 
@@ -397,6 +407,9 @@
                     writer.print(" mArgs="); writer.println(mArgs);
             writer.print(prefix); writer.print("mCallbacks="); writer.println(mCallbacks);
             writer.print(prefix); writer.print("mLoader="); writer.println(mLoader);
+            if (mLoader != null) {
+                mLoader.dump(prefix + "  ", fd, writer, args);
+            }
             writer.print(prefix); writer.print("mData="); writer.println(mData);
             writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
                     writer.print(" mRetaining="); writer.print(mRetaining);
@@ -434,12 +447,14 @@
     public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
         LoaderInfo info = mLoaders.get(id);
         
-        if (DEBUG) Log.v(TAG, "initLoader in " + this + ": cur=" + info);
+        if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
 
         if (info == null) {
             // Loader doesn't already exist; create.
             info = createLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+            if (DEBUG) Log.v(TAG, "  Created new loader " + info);
         } else {
+            if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);
             info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
         }
         
@@ -454,7 +469,7 @@
     @SuppressWarnings("unchecked")
     public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
         LoaderInfo info = mLoaders.get(id);
-        if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": cur=" + info);
+        if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": args=" + args);
         if (info != null) {
             LoaderInfo inactive = mInactiveLoaders.get(id);
             if (inactive != null) {
@@ -463,7 +478,7 @@
                     // called from within onLoadComplete, where we haven't
                     // yet destroyed the last inactive loader.  So just do
                     // that now.
-                    if (DEBUG) Log.v(TAG, "  Removing last inactive loader in " + this);
+                    if (DEBUG) Log.v(TAG, "  Removing last inactive loader: " + info);
                     inactive.mNeedReset = false;
                     inactive.destroy();
                     mInactiveLoaders.put(id, info);
@@ -472,13 +487,13 @@
                     // waiting for!  Now we have three active loaders... let's just
                     // drop the one in the middle, since we are still waiting for
                     // its result but that result is already out of date.
-                    if (DEBUG) Log.v(TAG, "  Removing intermediate loader in " + this);
+                    if (DEBUG) Log.v(TAG, "  Removing intermediate loader: " + info);
                     info.destroy();
                 }
             } else {
                 // Keep track of the previous instance of this loader so we can destroy
                 // it when the new one completes.
-                if (DEBUG) Log.v(TAG, "  Making inactive: " + info);
+                if (DEBUG) Log.v(TAG, "  Making last loader inactive: " + info);
                 mInactiveLoaders.put(id, info);
             }
         }
@@ -488,7 +503,7 @@
     }
     
     public void destroyLoader(int id) {
-        if (DEBUG) Log.v(TAG, "stopLoader in " + this + " of " + id);
+        if (DEBUG) Log.v(TAG, "destroyLoader in " + this + " of " + id);
         int idx = mLoaders.indexOfKey(id);
         if (idx >= 0) {
             LoaderInfo info = mLoaders.valueAt(idx);
@@ -513,7 +528,7 @@
     }
  
     void doStart() {
-        if (DEBUG) Log.v(TAG, "Starting: " + this);
+        if (DEBUG) Log.v(TAG, "Starting in " + this);
         if (mStarted) {
             RuntimeException e = new RuntimeException("here");
             e.fillInStackTrace();
@@ -531,7 +546,7 @@
     }
     
     void doStop() {
-        if (DEBUG) Log.v(TAG, "Stopping: " + this);
+        if (DEBUG) Log.v(TAG, "Stopping in " + this);
         if (!mStarted) {
             RuntimeException e = new RuntimeException("here");
             e.fillInStackTrace();
@@ -546,7 +561,7 @@
     }
     
     void doRetain() {
-        if (DEBUG) Log.v(TAG, "Retaining: " + this);
+        if (DEBUG) Log.v(TAG, "Retaining in " + this);
         if (!mStarted) {
             RuntimeException e = new RuntimeException("here");
             e.fillInStackTrace();
@@ -562,23 +577,25 @@
     }
     
     void finishRetain() {
-        if (DEBUG) Log.v(TAG, "Finished Retaining: " + this);
+        if (mRetaining) {
+            if (DEBUG) Log.v(TAG, "Finished Retaining in " + this);
 
-        mRetaining = false;
-        for (int i = mLoaders.size()-1; i >= 0; i--) {
-            mLoaders.valueAt(i).finishRetain();
+            mRetaining = false;
+            for (int i = mLoaders.size()-1; i >= 0; i--) {
+                mLoaders.valueAt(i).finishRetain();
+            }
         }
     }
     
     void doDestroy() {
         if (!mRetaining) {
-            if (DEBUG) Log.v(TAG, "Destroying Active: " + this);
+            if (DEBUG) Log.v(TAG, "Destroying Active in " + this);
             for (int i = mLoaders.size()-1; i >= 0; i--) {
                 mLoaders.valueAt(i).destroy();
             }
         }
         
-        if (DEBUG) Log.v(TAG, "Destroying Inactive: " + this);
+        if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);
         for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
             mInactiveLoaders.valueAt(i).destroy();
         }
@@ -586,6 +603,17 @@
     }
 
     @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("LoaderManager{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" in ");
+        DebugUtils.buildShortClassTag(mActivity, sb);
+        sb.append("}}");
+        return sb.toString();
+    }
+
+    @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         if (mLoaders.size() > 0) {
             writer.print(prefix); writer.println("Active Loaders:");
@@ -593,7 +621,7 @@
             for (int i=0; i < mLoaders.size(); i++) {
                 LoaderInfo li = mLoaders.valueAt(i);
                 writer.print(prefix); writer.print("  #"); writer.print(mLoaders.keyAt(i));
-                        writer.print(": "); writer.println(li.toBasicString());
+                        writer.print(": "); writer.println(li.toString());
                 li.dump(innerPrefix, fd, writer, args);
             }
         }
@@ -603,7 +631,7 @@
             for (int i=0; i < mInactiveLoaders.size(); i++) {
                 LoaderInfo li = mInactiveLoaders.valueAt(i);
                 writer.print(prefix); writer.print("  #"); writer.print(mInactiveLoaders.keyAt(i));
-                        writer.print(": "); writer.println(li.toBasicString());
+                        writer.print(": "); writer.println(li.toString());
                 li.dump(innerPrefix, fd, writer, args);
             }
         }
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index b19c072..30f8ffc 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -53,12 +53,8 @@
         super(context);
     }
 
-    /**
-     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
-     * loaded data set and load a new one.
-     */
     @Override
-    public void forceLoad() {
+    protected void onForceLoad() {
         cancelLoad();
         mTask = new LoadTask();
         mTask.execute((Void[]) null);
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 9e03c25..91dd23b 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -20,21 +20,25 @@
 import android.database.Cursor;
 import android.net.Uri;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
 /**
  * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
  */
 public class CursorLoader extends AsyncTaskLoader<Cursor> {
-    Cursor mCursor;
-    ForceLoadContentObserver mObserver;
-    boolean mStopped;
-    boolean mContentChanged;
-    boolean mReset;
+    final ForceLoadContentObserver mObserver;
+
     Uri mUri;
     String[] mProjection;
     String mSelection;
     String[] mSelectionArgs;
     String mSortOrder;
 
+    Cursor mCursor;
+    boolean mContentChanged;
+
     /* Runs on a worker thread */
     @Override
     public Cursor loadInBackground() {
@@ -69,7 +73,7 @@
         Cursor oldCursor = mCursor;
         mCursor = cursor;
 
-        if (!mStopped) {
+        if (isStarted()) {
             super.deliverResult(cursor);
         }
 
@@ -97,10 +101,7 @@
      * Must be called from the UI thread
      */
     @Override
-    public void startLoading() {
-        mStopped = false;
-        mReset = false;
-
+    protected void onStartLoading() {
         if (mCursor != null) {
             deliverResult(mCursor);
         }
@@ -114,17 +115,14 @@
      * Must be called from the UI thread
      */
     @Override
-    public void stopLoading() {
+    protected void onStopLoading() {
         // Attempt to cancel the current load task if possible.
         cancelLoad();
-
-        // Make sure that any outstanding loads clean themselves up properly
-        mStopped = true;
     }
 
     @Override
     public void onContentChanged() {
-        if (mStopped) {
+        if (!isStarted()) {
             // This loader has been stopped, so we don't want to load
             // new data right now...  but keep track of it changing to
             // refresh later if we start again.
@@ -142,7 +140,7 @@
     }
 
     @Override
-    public void reset() {
+    protected void onReset() {
         mReset = true;
 
         // Ensure the loader is stopped
@@ -193,4 +191,18 @@
     public void setSortOrder(String sortOrder) {
         mSortOrder = sortOrder;
     }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.print(prefix); writer.print("mUri="); writer.println(mUri);
+        writer.print(prefix); writer.print("mProjection=");
+                writer.println(Arrays.toString(mProjection));
+        writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
+        writer.print(prefix); writer.print("mSelectionArgs=");
+                writer.println(Arrays.toString(mSelectionArgs));
+        writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
+        writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
+        writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
+    }
 }
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 73d7103..f425b29 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -18,18 +18,27 @@
 
 import android.database.ContentObserver;
 import android.os.Handler;
+import android.util.DebugUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
 /**
  * An abstract class that performs asynchronous loading of data. While Loaders are active
  * they should monitor the source of their data and deliver new results when the contents
  * change.
  *
+ * <p>Subclasses generally must implement at least {@link #onStartLoading()},
+ * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.
+ *
  * @param <D> The result returned when the load is complete
  */
-public abstract class Loader<D> {
+public class Loader<D> {
     int mId;
     OnLoadCompleteListener<D> mListener;
     Context mContext;
+    boolean mStarted = false;
+    boolean mReset = true;
 
     public final class ForceLoadContentObserver extends ContentObserver {
         public ForceLoadContentObserver() {
@@ -122,28 +131,88 @@
     }
 
     /**
+     * Return whether this load has been started.  That is, its {@link #startLoading()}
+     * has been called and no calls to {@link #stopLoading()} or
+     * {@link #reset()} have yet been made.
+     */
+    public boolean isStarted() {
+        return mStarted;
+    }
+
+    /**
+     * Return whether this load has been reset.  That is, either the loader
+     * has not yet been started for the first time, or its {@link #reset()}
+     * has been called.
+     */
+    public boolean isReset() {
+        return mReset;
+    }
+
+    /**
      * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
      * will be called on the UI thread. If a previous load has been completed and is still valid
      * the result may be passed to the callbacks immediately. The loader will monitor the source of
      * the data set and may deliver future callbacks if the source changes. Calling
      * {@link #stopLoading} will stop the delivery of callbacks.
      *
-     * <p>Must be called from the UI thread
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onStartLoading()}.
+     *
+     * <p>Must be called from the UI thread.
      */
-    public abstract void startLoading();
+    public void startLoading() {
+        mStarted = true;
+        mReset = false;
+        onStartLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of loading their data,
+     * as per {@link #startLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #startLoading()}.
+     */
+    protected void onStartLoading() {
+    }
 
     /**
      * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
-     * loaded data set and load a new one.
+     * loaded data set and load a new one.  This simply calls through to the
+     * implementation's {@link #onForceLoad()}.
+     *
+     * <p>Must be called from the UI thread.
      */
-    public abstract void forceLoad();
+    public void forceLoad() {
+        onForceLoad();
+    }
 
     /**
-     * Stops delivery of updates until the next time {@link #startLoading()} is called
-     *
-     * <p>Must be called from the UI thread
+     * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
      */
-    public abstract void stopLoading();
+    protected void onForceLoad() {
+    }
+
+    /**
+     * Stops delivery of updates until the next time {@link #startLoading()} is called.
+     *
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} will return the correct
+     * value, and then calls the implementation's {@link #onStopLoading()}.
+     *
+     * <p>Must be called from the UI thread.
+     */
+    public void stopLoading() {
+        mStarted = false;
+        onStopLoading();
+    }
+
+    /**
+     * Subclasses must implement this to take care of stopping their loader,
+     * as per {@link #stopLoading()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #stopLoading()}.
+     */
+    protected void onStopLoading() {
+    }
 
     /**
      * Resets the state of the Loader.  The Loader should at this point free
@@ -151,17 +220,24 @@
      * {@link #startLoading()} may later be called at which point it must be
      * able to start running again.
      *
-     * <p>Must be called from the UI thread
+     * <p>This updates the Loader's internal state so that
+     * {@link #isStarted()} and {@link #isReset()} will return the correct
+     * values, and then calls the implementation's {@link #onReset()}.
+     *
+     * <p>Must be called from the UI thread.
      */
     public void reset() {
-        destroy();
+        onReset();
+        mReset = true;
+        mStarted = false;
     }
 
     /**
-     * @deprecated Old API, implement reset() now.
+     * Subclasses must implement this to take care of resetting their loader,
+     * as per {@link #reset()}.  This is not called by clients directly,
+     * but as a result of a call to {@link #reset()}.
      */
-    @Deprecated
-    public void destroy() {
+    protected void onReset() {
     }
 
     /**
@@ -173,4 +249,40 @@
     public void onContentChanged() {
         forceLoad();
     }
+
+    /**
+     * For debugging, converts an instance of the Loader's data class to
+     * a string that can be printed.  Must handle a null data.
+     */
+    public String dataToString(D data) {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(data, sb);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(64);
+        DebugUtils.buildShortClassTag(this, sb);
+        sb.append(" id=");
+        sb.append(mId);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /**
+     * Print the Loader's state into the given stream.
+     *
+     * @param prefix Text to print at the front of each line.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer A PrintWriter to which the dump is to be set.
+     * @param args Additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.print(prefix); writer.print("mId="); writer.print(mId);
+                writer.print(" mListener="); writer.println(mListener);
+        writer.print(prefix); writer.print("mStarted="); writer.print(mStarted);
+                writer.print(" mReset="); writer.println(mReset);
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index f0309d6..9d5d742 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -281,7 +281,7 @@
     /**
      * Runs on the UI thread after {@link #doInBackground}. The
      * specified result is the value returned by {@link #doInBackground}
-     * or null if the task was cancelled or an exception occured.
+     * or null if the task was cancelled or an exception occurred.
      *
      * @param result The result of the operation computed by {@link #doInBackground}.
      *
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 5f6c4f0..b6c2ed6 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -153,6 +153,7 @@
     private static native int createPipeNative(FileDescriptor[] outFds);
 
     /**
+     * @deprecated Please use createPipe() or ContentProvider.openPipeHelper().
      * Gets a file descriptor for a read-only copy of the given data.
      *
      * @param data Data to copy.
@@ -161,6 +162,7 @@
      * @return A ParcelFileDescriptor.
      * @throws IOException if there is an error while creating the shared memory area.
      */
+    @Deprecated
     public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
         if (data == null) return null;
         MemoryFile file = new MemoryFile(name, data.length);
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 56f389c..65fc35c 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -101,4 +101,23 @@
         return match;
     }
 
+    /** @hide */
+    public static void buildShortClassTag(Object cls, StringBuilder out) {
+        if (cls == null) {
+            out.append("null");
+        } else {
+            String simpleName = cls.getClass().getSimpleName();
+            if (simpleName == null || simpleName.isEmpty()) {
+                simpleName = cls.getClass().getName();
+                int end = simpleName.lastIndexOf('.');
+                if (end > 0) {
+                    simpleName = simpleName.substring(end+1);
+                }
+            }
+            out.append(simpleName);
+            out.append('{');
+            out.append(Integer.toHexString(System.identityHashCode(cls)));
+        }
+    }
+
 }