Remove dependency upon FragmentActivity

The FragmentManagerImpl is intimately tied with a FragmentActivity. In
many cases, we want to be able to create / manage Fragments outside of
a FragmentManager. This defines a FragmentController interface that can
be used by any class to host Fragments.

Bug: 19569096
Change-Id: I62dee733a70577d0d3c8f96a89e4b05a3d5e18b0
diff --git a/.gitignore b/.gitignore
index 08a55c0..dbc32d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
+.classpath
 .gradle
+.project
+.settings/
+project.properties
+**/bin
+**/gen
diff --git a/v4/AndroidManifest.xml b/v4/AndroidManifest.xml
index 08dbb61..a21d926 100644
--- a/v4/AndroidManifest.xml
+++ b/v4/AndroidManifest.xml
@@ -15,5 +15,6 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.support.v4">
+    <uses-sdk android:minSdkVersion="4"/>
     <application />
 </manifest>
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 02321a3..3d7df08 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -114,6 +114,7 @@
     method public boolean getAllowReturnTransitionOverlap();
     method public final android.os.Bundle getArguments();
     method public final android.support.v4.app.FragmentManager getChildFragmentManager();
+    method public final android.content.Context getContext();
     method public java.lang.Object getEnterTransition();
     method public java.lang.Object getExitTransition();
     method public final android.support.v4.app.FragmentManager getFragmentManager();
@@ -146,7 +147,8 @@
     method public final boolean isVisible();
     method public void onActivityCreated(android.os.Bundle);
     method public void onActivityResult(int, int, android.content.Intent);
-    method public void onAttach(android.app.Activity);
+    method public void onAttach(android.content.Context);
+    method public deprecated void onAttach(android.app.Activity);
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public boolean onContextItemSelected(android.view.MenuItem);
     method public void onCreate(android.os.Bundle);
@@ -159,7 +161,8 @@
     method public void onDestroyView();
     method public void onDetach();
     method public void onHiddenChanged(boolean);
-    method public void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
+    method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
+    method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
     method public void onLowMemory();
     method public boolean onOptionsItemSelected(android.view.MenuItem);
     method public void onOptionsMenuClosed(android.view.Menu);
@@ -212,7 +215,7 @@
     method public void onAttachFragment(android.support.v4.app.Fragment);
     method protected void onResumeFragments();
     method public java.lang.Object onRetainCustomNonConfigurationInstance();
-    method public final java.lang.Object onRetainNonConfigurationInstance();
+    method public final deprecated java.lang.Object onRetainNonConfigurationInstance();
     method public void setEnterSharedElementCallback(android.support.v4.app.SharedElementCallback);
     method public void setExitSharedElementCallback(android.support.v4.app.SharedElementCallback);
     method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
@@ -222,6 +225,59 @@
     method public void supportStartPostponedEnterTransition();
   }
 
+  public abstract class FragmentContainer {
+    ctor public FragmentContainer();
+    method public abstract android.view.View findViewById(int);
+    method public abstract boolean hasView();
+  }
+
+  public class FragmentController {
+    method public void attachHost(android.support.v4.app.Fragment);
+    method public static final android.support.v4.app.FragmentController createController(android.support.v4.app.FragmentHostCallbacks);
+    method public void dispatchActivityCreated();
+    method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public boolean dispatchContextItemSelected(android.view.MenuItem);
+    method public void dispatchCreate();
+    method public boolean dispatchCreateOptionsMenu(android.view.Menu, android.view.MenuInflater);
+    method public void dispatchDestroy();
+    method public void dispatchDestroyView();
+    method public void dispatchLowMemory();
+    method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
+    method public void dispatchOptionsMenuClosed(android.view.Menu);
+    method public void dispatchPause();
+    method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
+    method public void dispatchReallyStop();
+    method public void dispatchResume();
+    method public void dispatchStart();
+    method public void dispatchStop();
+    method public void dumpLoaders(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public boolean execPendingActions();
+    method public java.util.List<android.support.v4.app.Fragment> getActiveFragments(java.util.List<android.support.v4.app.Fragment>);
+    method public int getActiveFragmentsCount();
+    method public android.support.v4.app.FragmentManager getSupportFragmentManager();
+    method public android.support.v4.app.LoaderManager getSupportLoaderManager();
+    method public void noteStateNotSaved();
+    method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
+    method public void restoreAllState(android.os.Parcelable, java.util.ArrayList<android.support.v4.app.Fragment>);
+    method public void restoreLoaderNonConfig(android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager>);
+    method public android.support.v4.util.SimpleArrayMap<java.lang.String, android.support.v4.app.LoaderManager> retainLoaderNonConfig();
+    method public java.util.ArrayList<android.support.v4.app.Fragment> retainNonConfig();
+    method public android.os.Parcelable saveAllState();
+  }
+
+  public class FragmentHostCallbacks extends android.support.v4.app.FragmentContainer {
+    ctor public FragmentHostCallbacks(android.content.Context, android.os.Handler, int);
+    method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method public android.view.View findViewById(int);
+    method public android.view.LayoutInflater getLayoutInflater();
+    method public int getWindowAnimations();
+    method public boolean hasView();
+    method public boolean hasWindowAnimations();
+    method public boolean shouldSaveFragmentState(android.support.v4.app.Fragment);
+    method public void startActivityFromFragment(android.support.v4.app.Fragment, android.content.Intent, int);
+    method public void supportInvalidateOptionsMenu();
+  }
+
   public abstract class FragmentManager {
     ctor public FragmentManager();
     method public abstract void addOnBackStackChangedListener(android.support.v4.app.FragmentManager.OnBackStackChangedListener);
diff --git a/v4/java/android/support/v4/app/BackStackRecord.java b/v4/java/android/support/v4/app/BackStackRecord.java
index 25f3ebf..32847ec 100644
--- a/v4/java/android/support/v4/app/BackStackRecord.java
+++ b/v4/java/android/support/v4/app/BackStackRecord.java
@@ -16,12 +16,10 @@
 
 package android.support.v4.app;
 
-import android.graphics.Rect;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.v4.util.LogWriter;
-import android.support.v4.util.Pair;
 import android.support.v4.util.ArrayMap;
 import android.text.TextUtils;
 import android.util.Log;
@@ -33,7 +31,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Collection;
 
 final class BackStackState implements Parcelable {
     final int[] mOps;
@@ -48,7 +45,7 @@
     final ArrayList<String> mSharedElementSourceNames;
     final ArrayList<String> mSharedElementTargetNames;
 
-    public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
+    public BackStackState(BackStackRecord bse) {
         int numRemoved = 0;
         BackStackRecord.Op op = bse.mHead;
         while (op != null) {
@@ -371,14 +368,14 @@
 
     public CharSequence getBreadCrumbTitle() {
         if (mBreadCrumbTitleRes != 0) {
-            return mManager.mActivity.getText(mBreadCrumbTitleRes);
+            return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
         }
         return mBreadCrumbTitleText;
     }
 
     public CharSequence getBreadCrumbShortTitle() {
         if (mBreadCrumbShortTitleRes != 0) {
-            return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
+            return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
         }
         return mBreadCrumbShortTitleText;
     }
@@ -1023,7 +1020,7 @@
         // Adding a non-existent target view makes sure that the transitions don't target
         // any views by default. They'll only target the views we tell add. If we don't
         // add any, then no views will be targeted.
-        state.nonExistentView = new View(mManager.mActivity);
+        state.nonExistentView = new View(mManager.mHost.getContext());
 
         boolean anyTransitionStarted = false;
         // Go over all leaving fragments.
diff --git a/v4/java/android/support/v4/app/DialogFragment.java b/v4/java/android/support/v4/app/DialogFragment.java
index 343c145..996d19f 100644
--- a/v4/java/android/support/v4/app/DialogFragment.java
+++ b/v4/java/android/support/v4/app/DialogFragment.java
@@ -320,7 +320,7 @@
             return (LayoutInflater) mDialog.getContext().getSystemService(
                     Context.LAYOUT_INFLATER_SERVICE);
         }
-        return (LayoutInflater) mActivity.getSystemService(
+        return (LayoutInflater) mHost.getContext().getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
     }
     
diff --git a/v4/java/android/support/v4/app/Fragment.java b/v4/java/android/support/v4/app/Fragment.java
index eb6ab0d..2187777 100644
--- a/v4/java/android/support/v4/app/Fragment.java
+++ b/v4/java/android/support/v4/app/Fragment.java
@@ -22,8 +22,6 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -89,20 +87,21 @@
         mArguments = in.readBundle();
         mSavedFragmentState = in.readBundle();
     }
-    
-    public Fragment instantiate(FragmentActivity activity, Fragment parent) {
+
+    public Fragment instantiate(FragmentHostCallbacks host, Fragment parent) {
         if (mInstance != null) {
             return mInstance;
         }
-        
+
+        final Context context = host.getContext();
         if (mArguments != null) {
-            mArguments.setClassLoader(activity.getClassLoader());
+            mArguments.setClassLoader(context.getClassLoader());
         }
-        
-        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
-        
+
+        mInstance = Fragment.instantiate(context, mClassName, mArguments);
+
         if (mSavedFragmentState != null) {
-            mSavedFragmentState.setClassLoader(activity.getClassLoader());
+            mSavedFragmentState.setClassLoader(context.getClassLoader());
             mInstance.mSavedFragmentState = mSavedFragmentState;
         }
         mInstance.setIndex(mIndex, parent);
@@ -113,14 +112,14 @@
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
         mInstance.mDetached = mDetached;
-        mInstance.mFragmentManager = activity.mFragments;
+        mInstance.mFragmentManager = host.mFragmentManager;
 
         if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                 "Instantiated fragment " + mInstance);
 
         return mInstance;
     }
-    
+
     public int describeContents() {
         return 0;
     }
@@ -228,17 +227,17 @@
 
     // True if this fragment has been restored from previously saved state.
     boolean mRestored;
-    
+
     // Number of active back stack entries this fragment is in.
     int mBackStackNesting;
-    
+
     // The fragment manager we are associated with.  Set as soon as the
     // fragment is used in a transaction; cleared after it has been removed
     // from all transactions.
     FragmentManagerImpl mFragmentManager;
 
-    // Activity this fragment is attached to.
-    FragmentActivity mActivity;
+    // Host this fragment is attached to.
+    FragmentHostCallbacks mHost;
 
     // Private fragment manager for child fragments inside of this one.
     FragmentManagerImpl mChildFragmentManager;
@@ -348,10 +347,12 @@
 
         public static final Parcelable.Creator<SavedState> CREATOR
                 = new Parcelable.Creator<SavedState>() {
+            @Override
             public SavedState createFromParcel(Parcel in) {
                 return new SavedState(in, null);
             }
 
+            @Override
             public SavedState[] newArray(int size) {
                 return new SavedState[size];
             }
@@ -606,22 +607,31 @@
     }
 
     /**
-     * Return the Activity this fragment is currently associated with.
+     * Return the {@link Context} this fragment is currently associated with.
+     */
+    final public Context getContext() {
+        return mHost == null ? null : mHost.getContext();
+    }
+
+    /**
+     * Return the {@link FragmentActivity} this fragment is currently associated with.
+     * May return {@code null} if the fragment is associated with a {@link Context}
+     * instead.
      */
     final public FragmentActivity getActivity() {
-        return mActivity;
+        return mHost == null ? null : (FragmentActivity) mHost.getActivity();
     }
-    
+
     /**
      * Return <code>getActivity().getResources()</code>.
      */
     final public Resources getResources() {
-        if (mActivity == null) {
+        if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
-        return mActivity.getResources();
+        return mHost.getContext().getResources();
     }
-    
+
     /**
      * Return a localized, styled CharSequence from the application's package's
      * default string table.
@@ -701,7 +711,7 @@
      * Return true if the fragment is currently added to its activity.
      */
     final public boolean isAdded() {
-        return mActivity != null && mAdded;
+        return mHost != null && mAdded;
     }
 
     /**
@@ -812,14 +822,14 @@
      * Report that this fragment would like to participate in populating
      * the options menu by receiving a call to {@link #onCreateOptionsMenu}
      * and related methods.
-     * 
+     *
      * @param hasMenu If true, the fragment has menu items to contribute.
      */
     public void setHasOptionsMenu(boolean hasMenu) {
         if (mHasMenu != hasMenu) {
             mHasMenu = hasMenu;
             if (isAdded() && !isHidden()) {
-                mActivity.supportInvalidateOptionsMenu();
+                mHost.supportInvalidateOptionsMenu();
             }
         }
     }
@@ -837,7 +847,7 @@
         if (mMenuVisible != menuVisible) {
             mMenuVisible = menuVisible;
             if (mHasMenu && isAdded() && !isHidden()) {
-                mActivity.supportInvalidateOptionsMenu();
+                mHost.supportInvalidateOptionsMenu();
             }
         }
     }
@@ -878,42 +888,42 @@
         if (mLoaderManager != null) {
             return mLoaderManager;
         }
-        if (mActivity == null) {
+        if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
         mCheckedForLoaderManager = true;
-        mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, true);
+        mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);
         return mLoaderManager;
     }
-    
+
     /**
      * Call {@link Activity#startActivity(Intent)} on the fragment's
      * containing Activity.
      */
     public void startActivity(Intent intent) {
-        if (mActivity == null) {
+        if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
-        mActivity.startActivityFromFragment(this, intent, -1);
+        mHost.startActivityFromFragment(this /*fragment*/, intent, -1);
     }
-    
+
     /**
      * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
      * containing Activity.
      */
     public void startActivityForResult(Intent intent, int requestCode) {
-        if (mActivity == null) {
+        if (mHost == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
-        mActivity.startActivityFromFragment(this, intent, requestCode);
+        mHost.startActivityFromFragment(this /*fragment*/, intent, requestCode);
     }
-    
+
     /**
      * Receive the result from a previous call to
      * {@link #startActivityForResult(Intent, int)}.  This follows the
      * related Activity API as described there in
      * {@link Activity#onActivityResult(int, int, Intent)}.
-     * 
+     *
      * @param requestCode The integer request code originally supplied to
      *                    startActivityForResult(), allowing you to identify who this
      *                    result came from.
@@ -924,19 +934,19 @@
      */
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
     }
-    
+
     /**
      * @hide Hack so that DialogFragment can make its Dialog before creating
      * its views, and the view construction can use the dialog's context for
      * inflation.  Maybe this should become a public API. Note sure.
      */
     public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
-        LayoutInflater result = mActivity.getLayoutInflater().cloneInContext(mActivity);
+        LayoutInflater result = mHost.getLayoutInflater();
         getChildFragmentManager(); // Init if needed; use raw implementation below.
         LayoutInflaterCompat.setFactory(result, mChildFragmentManager.getLayoutInflaterFactory());
         return result;
     }
-    
+
     /**
      * Called when a fragment is being created as part of a view layout
      * inflation, typically from setting the content view of an activity.  This
@@ -973,31 +983,61 @@
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentArguments.java
      *      create}
      *
-     * @param activity The Activity that is inflating this fragment.
+     * @param context The Activity that is inflating this fragment.
      * @param attrs The attributes at the tag where the fragment is
      * being created.
      * @param savedInstanceState If the fragment is being re-created from
      * a previous saved state, this is the state.
      */
+    public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
+        mCalled = true;
+        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+        if (hostActivity != null) {
+            mCalled = false;
+            onInflate(hostActivity, attrs, savedInstanceState);
+        }
+    }
+
+    /**
+     * Called when a fragment is being created as part of a view layout
+     * inflation, typically from setting the content view of an activity.
+     * <p>Deprecated. See {@link #onInflate(Context, AttributeSet, Bundle)}.
+     */
+    @Deprecated
     public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
         mCalled = true;
     }
 
     /**
-     * Called when a fragment is first attached to its activity.
+     * Called when a fragment is first attached to its context.
      * {@link #onCreate(Bundle)} will be called after this.
      */
+    public void onAttach(Context context) {
+        mCalled = true;
+        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+        if (hostActivity != null) {
+            mCalled = false;
+            onAttach(hostActivity);
+        }
+    }
+
+    /**
+     * Called when a fragment is first attached to its activity.
+     * {@link #onCreate(Bundle)} will be called after this.
+     * <p>Deprecated. See {@link #onAttach(Context)}.
+     */
+    @Deprecated
     public void onAttach(Activity activity) {
         mCalled = true;
     }
-    
+
     /**
      * Called when a fragment loads an animation.
      */
     public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
         return null;
     }
-    
+
     /**
      * Called to do initial creation of a fragment.  This is called after
      * {@link #onAttach(Activity)} and before
@@ -1104,19 +1144,19 @@
      */
     public void onStart() {
         mCalled = true;
-        
+
         if (!mLoadersStarted) {
             mLoadersStarted = true;
             if (!mCheckedForLoaderManager) {
                 mCheckedForLoaderManager = true;
-                mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+                mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
             }
             if (mLoaderManager != null) {
                 mLoaderManager.doStart();
             }
         }
     }
-    
+
     /**
      * Called when the fragment is visible to the user and actively running.
      * This is generally
@@ -1198,7 +1238,7 @@
         //        + " mLoaderManager=" + mLoaderManager);
         if (!mCheckedForLoaderManager) {
             mCheckedForLoaderManager = true;
-            mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+            mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
         }
         if (mLoaderManager != null) {
             mLoaderManager.doDestroy();
@@ -1223,7 +1263,7 @@
         mBackStackNesting = 0;
         mFragmentManager = null;
         mChildFragmentManager = null;
-        mActivity = null;
+        mHost = null;
         mFragmentId = 0;
         mContainerId = 0;
         mTag = null;
@@ -1335,6 +1375,7 @@
      * It is not safe to hold onto the context menu after this method returns.
      * {@inheritDoc}
      */
+    @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
         getActivity().onCreateContextMenu(menu, v, menuInfo);
     }
@@ -1678,9 +1719,9 @@
             writer.print(prefix); writer.print("mFragmentManager=");
                     writer.println(mFragmentManager);
         }
-        if (mActivity != null) {
-            writer.print(prefix); writer.print("mActivity=");
-                    writer.println(mActivity);
+        if (mHost != null) {
+            writer.print(prefix); writer.print("mHost=");
+                    writer.println(mHost);
         }
         if (mParentFragment != null) {
             writer.print(prefix); writer.print("mParentFragment=");
@@ -1741,7 +1782,7 @@
 
     void instantiateChildFragmentManager() {
         mChildFragmentManager = new FragmentManagerImpl();
-        mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
+        mChildFragmentManager.attachController(mHost, new FragmentContainer() {
             @Override
             @Nullable
             public View findViewById(int id) {
@@ -1974,10 +2015,10 @@
             mLoadersStarted = false;
             if (!mCheckedForLoaderManager) {
                 mCheckedForLoaderManager = true;
-                mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+                mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
             }
             if (mLoaderManager != null) {
-                if (!mActivity.mRetaining) {
+                if (!mRetaining) {
                     mLoaderManager.doStop();
                 } else {
                     mLoaderManager.doRetain();
diff --git a/v4/java/android/support/v4/app/FragmentActivity.java b/v4/java/android/support/v4/app/FragmentActivity.java
index 566ffed..f58f41b 100644
--- a/v4/java/android/support/v4/app/FragmentActivity.java
+++ b/v4/java/android/support/v4/app/FragmentActivity.java
@@ -31,6 +31,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -40,6 +41,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Base class for activities that want to use the support-based
@@ -75,9 +77,9 @@
  */
 public class FragmentActivity extends Activity {
     private static final String TAG = "FragmentActivity";
-    
+
     static final String FRAGMENTS_TAG = "android:support:fragments";
-    
+
     // This is the SDK API version of Honeycomb (3.0).
     private static final int HONEYCOMB = 11;
 
@@ -103,21 +105,8 @@
         }
 
     };
-    final FragmentManagerImpl mFragments = new FragmentManagerImpl();
-    final FragmentContainer mContainer = new FragmentContainer() {
-        @Override
-        @Nullable
-        public View findViewById(int id) {
-            return FragmentActivity.this.findViewById(id);
-        }
+    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
 
-        @Override
-        public boolean hasView() {
-            Window window = FragmentActivity.this.getWindow();
-            return (window != null && window.peekDecorView() != null);
-        }
-    };
-    
     boolean mCreated;
     boolean mResumed;
     boolean mStopped;
@@ -126,23 +115,16 @@
 
     boolean mOptionsMenuInvalidated;
 
-    boolean mCheckedForLoaderManager;
-    boolean mLoadersStarted;
-    SimpleArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
-    LoaderManagerImpl mLoaderManager;
-
     static final class NonConfigurationInstances {
-        Object activity;
         Object custom;
-        SimpleArrayMap<String, Object> children;
         ArrayList<Fragment> fragments;
-        SimpleArrayMap<String, LoaderManagerImpl> loaders;
+        SimpleArrayMap<String, LoaderManager> loaders;
     }
-    
+
     // ------------------------------------------------------------------------
     // HOOKS INTO ACTIVITY
     // ------------------------------------------------------------------------
-    
+
     /**
      * Dispatch incoming result to the correct fragment.
      */
@@ -152,12 +134,15 @@
         int index = requestCode>>16;
         if (index != 0) {
             index--;
-            if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
+            final int activeFragmentsCount = mFragments.getActiveFragmentsCount();
+            if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) {
                 Log.w(TAG, "Activity result fragment index out of range: 0x"
                         + Integer.toHexString(requestCode));
                 return;
             }
-            Fragment frag = mFragments.mActive.get(index);
+            final List<Fragment> activeFragments =
+                    mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount));
+            Fragment frag = activeFragments.get(index);
             if (frag == null) {
                 Log.w(TAG, "Activity result no fragment exists for index: 0x"
                         + Integer.toHexString(requestCode));
@@ -166,7 +151,7 @@
             }
             return;
         }
-        
+
         super.onActivityResult(requestCode, resultCode, data);
     }
 
@@ -175,7 +160,7 @@
      * as appropriate.
      */
     public void onBackPressed() {
-        if (!mFragments.popBackStackImmediate()) {
+        if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
             supportFinishAfterTransition();
         }
     }
@@ -246,20 +231,21 @@
     /**
      * Perform initialization of all fragments and loaders.
      */
+    @SuppressWarnings("deprecation")
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
-        mFragments.attachActivity(this, mContainer, null);
+        mFragments.attachHost(null /*parent*/);
         // Old versions of the platform didn't do this!
         if (getLayoutInflater().getFactory() == null) {
             getLayoutInflater().setFactory(this);
         }
-        
+
         super.onCreate(savedInstanceState);
-        
-        NonConfigurationInstances nc = (NonConfigurationInstances)
-                getLastNonConfigurationInstance();
+
+        NonConfigurationInstances nc =
+                (NonConfigurationInstances) getLastNonConfigurationInstance();
         if (nc != null) {
-            mAllLoaderManagers = nc.loaders;
+            mFragments.restoreLoaderNonConfig(nc.loaders);
         }
         if (savedInstanceState != null) {
             Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
@@ -286,7 +272,7 @@
         }
         return super.onCreatePanelMenu(featureId, menu);
     }
-    
+
     /**
      * Add support for inflating the &lt;fragment> tag.
      */
@@ -314,9 +300,7 @@
         doReallyStop(false);
 
         mFragments.dispatchDestroy();
-        if (mLoaderManager != null) {
-            mLoaderManager.doDestroy();
-        }
+        mFragments.doLoaderDestroy();
     }
 
     /**
@@ -476,7 +460,9 @@
      * Retain all appropriate fragment and loader state.  You can NOT
      * override this yourself!  Use {@link #onRetainCustomNonConfigurationInstance()}
      * if you want to retain your own state.
+     * @deprecated
      */
+    @Deprecated
     @Override
     public final Object onRetainNonConfigurationInstance() {
         if (mStopped) {
@@ -486,35 +472,16 @@
         Object custom = onRetainCustomNonConfigurationInstance();
 
         ArrayList<Fragment> fragments = mFragments.retainNonConfig();
-        boolean retainLoaders = false;
-        if (mAllLoaderManagers != null) {
-            // prune out any loader managers that were already stopped and so
-            // have nothing useful to retain.
-            final int N = mAllLoaderManagers.size();
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
-            for (int i=N-1; i>=0; i--) {
-                loaders[i] = mAllLoaderManagers.valueAt(i);
-            }
-            for (int i=0; i<N; i++) {
-                LoaderManagerImpl lm = loaders[i];
-                if (lm.mRetaining) {
-                    retainLoaders = true;
-                } else {
-                    lm.doDestroy();
-                    mAllLoaderManagers.remove(lm.mWho);
-                }
-            }
-        }
-        if (fragments == null && !retainLoaders && custom == null) {
+        SimpleArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
+
+        if (fragments == null && loaders == null && custom == null) {
             return null;
         }
-        
+
         NonConfigurationInstances nci = new NonConfigurationInstances();
-        nci.activity = null;
         nci.custom = custom;
-        nci.children = null;
         nci.fragments = fragments;
-        nci.loaders = mAllLoaderManagers;
+        nci.loaders = loaders;
         return nci;
     }
 
@@ -549,35 +516,13 @@
 
         mFragments.noteStateNotSaved();
         mFragments.execPendingActions();
-        
-        if (!mLoadersStarted) {
-            mLoadersStarted = true;
-            if (mLoaderManager != null) {
-                mLoaderManager.doStart();
-            } else if (!mCheckedForLoaderManager) {
-                mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
-                // the returned loader manager may be a new one, so we have to start it
-                if ((mLoaderManager != null) && (!mLoaderManager.mStarted)) {
-                    mLoaderManager.doStart();
-                }
-            }
-            mCheckedForLoaderManager = true;
-        }
+
+        mFragments.doLoaderStart();
+
         // NOTE: HC onStart goes here.
-        
+
         mFragments.dispatchStart();
-        if (mAllLoaderManagers != null) {
-            final int N = mAllLoaderManagers.size();
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
-            for (int i=N-1; i>=0; i--) {
-                loaders[i] = mAllLoaderManagers.valueAt(i);
-            }
-            for (int i=0; i<N; i++) {
-                LoaderManagerImpl lm = loaders[i];
-                lm.finishRetain();
-                lm.doReportStart();
-            }
-        }
+        mFragments.reportLoaderStart();
     }
 
     /**
@@ -589,14 +534,14 @@
 
         mStopped = true;
         mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
-        
+
         mFragments.dispatchStop();
     }
 
     // ------------------------------------------------------------------------
     // NEW METHODS
     // ------------------------------------------------------------------------
-    
+
     /**
      * Use this instead of {@link #onRetainNonConfigurationInstance()}.
      * Retrieve later with {@link #getLastCustomNonConfigurationInstance()}.
@@ -609,6 +554,7 @@
      * Return the value previously returned from
      * {@link #onRetainCustomNonConfigurationInstance()}.
      */
+    @SuppressWarnings("deprecation")
     public Object getLastCustomNonConfigurationInstance() {
         NonConfigurationInstances nc = (NonConfigurationInstances)
                 getLastNonConfigurationInstance();
@@ -659,15 +605,8 @@
                 writer.print(mResumed); writer.print(" mStopped=");
                 writer.print(mStopped); writer.print(" mReallyStopped=");
                 writer.println(mReallyStopped);
-        writer.print(innerPrefix); writer.print("mLoadersStarted=");
-                writer.println(mLoadersStarted);
-        if (mLoaderManager != null) {
-            writer.print(prefix); writer.print("Loader Manager ");
-                    writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
-                    writer.println(":");
-            mLoaderManager.dump(prefix + "  ", fd, writer, args);
-        }
-        mFragments.dump(prefix, fd, writer, args);
+        mFragments.dumpLoaders(innerPrefix, fd, writer, args);
+        mFragments.getSupportFragmentManager().dump(prefix, fd, writer, args);
         writer.print(prefix); writer.println("View Hierarchy:");
         dumpViewHierarchy(prefix + "  ", writer, getWindow().getDecorView());
     }
@@ -776,16 +715,7 @@
      * tell us what we need to know.
      */
     void onReallyStop() {
-        if (mLoadersStarted) {
-            mLoadersStarted = false;
-            if (mLoaderManager != null) {
-                if (!mRetaining) {
-                    mLoaderManager.doStop();
-                } else {
-                    mLoaderManager.doRetain();
-                }
-            }
-        }
+        mFragments.doLoaderStop(mRetaining);
 
         mFragments.dispatchReallyStop();
     }
@@ -793,19 +723,24 @@
     // ------------------------------------------------------------------------
     // FRAGMENT SUPPORT
     // ------------------------------------------------------------------------
-    
+
     /**
      * Called when a fragment is attached to the activity.
      */
+    @SuppressWarnings("unused")
     public void onAttachFragment(Fragment fragment) {
     }
-    
+
     /**
      * Return the FragmentManager for interacting with fragments associated
      * with this activity.
      */
     public FragmentManager getSupportFragmentManager() {
-        return mFragments;
+        return mFragments.getSupportFragmentManager();
+    }
+
+    public LoaderManager getSupportLoaderManager() {
+        return mFragments.getSupportLoaderManager();
     }
 
     /**
@@ -823,7 +758,7 @@
     /**
      * Called by Fragment.startActivityForResult() to implement its behavior.
      */
-    public void startActivityFromFragment(Fragment fragment, Intent intent, 
+    public void startActivityFromFragment(Fragment fragment, Intent intent,
             int requestCode) {
         if (requestCode == -1) {
             super.startActivityForResult(intent, -1);
@@ -834,47 +769,58 @@
         }
         super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
     }
-    
-    void invalidateSupportFragment(String who) {
-        //Log.v(TAG, "invalidateSupportFragment: who=" + who);
-        if (mAllLoaderManagers != null) {
-            LoaderManagerImpl lm = mAllLoaderManagers.get(who);
-            if (lm != null && !lm.mRetaining) {
-                lm.doDestroy();
-                mAllLoaderManagers.remove(who);
-            }
+
+    class HostCallbacks extends FragmentHostCallbacks {
+        public HostCallbacks() {
+            super(FragmentActivity.this /*fragmentActivity*/);
         }
-    }
-    
-    // ------------------------------------------------------------------------
-    // LOADER SUPPORT
-    // ------------------------------------------------------------------------
-    
-    /**
-     * Return the LoaderManager for this fragment, creating it if needed.
-     */
-    public LoaderManager getSupportLoaderManager() {
-        if (mLoaderManager != null) {
-            return mLoaderManager;
+
+        @Override
+        public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+            FragmentActivity.this.dump(prefix, fd, writer, args);
         }
-        mCheckedForLoaderManager = true;
-        mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
-        return mLoaderManager;
-    }
-    
-    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
-        if (mAllLoaderManagers == null) {
-            mAllLoaderManagers = new SimpleArrayMap<String, LoaderManagerImpl>();
+
+        @Override
+        public boolean shouldSaveFragmentState(Fragment fragment) {
+            return !isFinishing();
         }
-        LoaderManagerImpl lm = mAllLoaderManagers.get(who);
-        if (lm == null) {
-            if (create) {
-                lm = new LoaderManagerImpl(who, this, started);
-                mAllLoaderManagers.put(who, lm);
-            }
-        } else {
-            lm.updateActivity(this);
+
+        @Override
+        public LayoutInflater getLayoutInflater() {
+            return FragmentActivity.this.getLayoutInflater().cloneInContext(FragmentActivity.this);
         }
-        return lm;
+
+        @Override
+        public void supportInvalidateOptionsMenu() {
+            FragmentActivity.this.supportInvalidateOptionsMenu();
+        }
+
+        @Override
+        public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
+            FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
+        }
+
+        @Override
+        public boolean hasWindowAnimations() {
+            return getWindow() != null;
+        }
+
+        @Override
+        public int getWindowAnimations() {
+            final Window w = getWindow();
+            return (w == null) ? 0 : w.getAttributes().windowAnimations;
+        }
+
+        @Nullable
+        @Override
+        public View findViewById(int id) {
+            return FragmentActivity.this.findViewById(id);
+        }
+
+        @Override
+        public boolean hasView() {
+            final Window w = getWindow();
+            return (w != null && w.peekDecorView() != null);
+        }
     }
 }
diff --git a/v4/java/android/support/v4/app/FragmentContainer.java b/v4/java/android/support/v4/app/FragmentContainer.java
new file mode 100644
index 0000000..74cd7cc
--- /dev/null
+++ b/v4/java/android/support/v4/app/FragmentContainer.java
@@ -0,0 +1,23 @@
+package android.support.v4.app;
+
+import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
+import android.view.View;
+
+
+/**
+ * Callbacks to a {@link Fragment}'s container.
+ */
+public abstract class FragmentContainer {
+    /**
+     * Return the view with the given resource ID. May return {@code null} if the
+     * view is not a child of this container.
+     */
+    @Nullable
+    public abstract View findViewById(@IdRes int id);
+
+    /**
+     * Return {@code true} if the container holds any view.
+     */
+    public abstract boolean hasView();
+}
diff --git a/v4/java/android/support/v4/app/FragmentController.java b/v4/java/android/support/v4/app/FragmentController.java
new file mode 100644
index 0000000..bd2da21
--- /dev/null
+++ b/v4/java/android/support/v4/app/FragmentController.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Parcelable;
+import android.support.v4.util.SimpleArrayMap;
+import android.util.AttributeSet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides integration points with a {@link FragmentManager}. For example, a fragment
+ * host, such as {@link FragmentActivity}, uses the {@link FragmentController} to control
+ * the {@link Fragment} lifecycle.
+ */
+public class FragmentController {
+    private final FragmentHostCallbacks mHost;
+
+    /**
+     * Returns a {@link FragmentController}.
+     */
+    public static final FragmentController createController(FragmentHostCallbacks callbacks) {
+        return new FragmentController(callbacks);
+    }
+
+    private FragmentController(FragmentHostCallbacks callbacks) {
+        mHost = callbacks;
+    }
+
+    public FragmentManager getSupportFragmentManager() {
+        return mHost.getFragmentManagerImpl();
+    }
+
+    public LoaderManager getSupportLoaderManager() {
+        return mHost.getLoaderManagerImpl();
+    }
+
+    /** Returns the number of active fragments. */
+    public int getActiveFragmentsCount() {
+        final List<Fragment> actives = mHost.mFragmentManager.mActive;
+        return actives == null ? 0 : actives.size();
+    }
+
+    /** Returns the list of active fragments. */
+    public List<Fragment> getActiveFragments(List<Fragment> actives) {
+        if (mHost.mFragmentManager.mActive == null) {
+            return null;
+        }
+        if (actives == null) {
+            actives = new ArrayList<Fragment>(getActiveFragmentsCount());
+        }
+        actives.addAll(mHost.mFragmentManager.mActive);
+        return actives;
+    }
+
+    /** Attaches the host to the FragmentManager. */
+    public void attachHost(Fragment parent) {
+        mHost.mFragmentManager.attachController(
+                mHost, mHost /*container*/, parent);
+    }
+
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
+    }
+
+    /**
+     * Marks the fragment state as unsaved. This allows for "state loss" detection.
+     */
+    public void noteStateNotSaved() {
+        mHost.mFragmentManager.noteStateNotSaved();
+    }
+
+    /**
+     * Saves the state for all Fragments.
+     */
+    public Parcelable saveAllState() {
+        return mHost.mFragmentManager.saveAllState();
+    }
+
+    /**
+     * Restores the saved state for all Fragments. The given Fragment list are Fragment
+     * instances retained across configuration changes.
+     *
+     * @see #retainNonConfig()
+     */
+    public void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfigList) {
+        mHost.mFragmentManager.restoreAllState(state, nonConfigList);
+    }
+
+    /**
+     * Returns a list of Fragments that have opted to retain their instance across
+     * configuration changes.
+     */
+    public ArrayList<Fragment> retainNonConfig() {
+        return mHost.mFragmentManager.retainNonConfig();
+    }
+
+    public void dispatchCreate() {
+        mHost.mFragmentManager.dispatchCreate();
+    }
+
+    public void dispatchActivityCreated() {
+        mHost.mFragmentManager.dispatchActivityCreated();
+    }
+
+    public void dispatchStart() {
+        mHost.mFragmentManager.dispatchStart();
+    }
+
+    public void dispatchResume() {
+        mHost.mFragmentManager.dispatchResume();
+    }
+
+    public void dispatchPause() {
+        mHost.mFragmentManager.dispatchPause();
+    }
+
+    public void dispatchStop() {
+        mHost.mFragmentManager.dispatchStop();
+    }
+
+    public void dispatchReallyStop() {
+        mHost.mFragmentManager.dispatchReallyStop();
+    }
+
+    public void dispatchDestroyView() {
+        mHost.mFragmentManager.dispatchDestroyView();
+    }
+
+    public void dispatchDestroy() {
+        mHost.mFragmentManager.dispatchDestroy();
+    }
+
+    public void dispatchConfigurationChanged(Configuration newConfig) {
+        mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
+    }
+
+    public void dispatchLowMemory() {
+        mHost.mFragmentManager.dispatchLowMemory();
+    }
+
+    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
+    }
+
+    public boolean dispatchPrepareOptionsMenu(Menu menu) {
+        return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
+    }
+
+    public boolean dispatchOptionsItemSelected(MenuItem item) {
+        return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
+    }
+
+    public boolean dispatchContextItemSelected(MenuItem item) {
+        return mHost.mFragmentManager.dispatchContextItemSelected(item);
+    }
+
+    public void dispatchOptionsMenuClosed(Menu menu) {
+        mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
+    }
+
+    public boolean execPendingActions() {
+        return mHost.mFragmentManager.execPendingActions();
+    }
+
+    void doLoaderStart() {
+        mHost.doLoaderStart();
+    }
+
+    /**
+     * Stops the loaders, optionally retaining their state. This is useful for keeping the
+     * loader state across configuration changes.
+     *
+     * @param retain When {@code true}, the loaders aren't stopped, but, their instances
+     * are retained in a started state
+     */
+    void doLoaderStop(boolean retain) {
+        mHost.doLoaderStop(retain);
+    }
+
+    void doLoaderRetain() {
+        mHost.doLoaderRetain();
+    }
+
+    void doLoaderDestroy() {
+        mHost.doLoaderDestroy();
+    }
+
+    void reportLoaderStart() {
+        mHost.reportLoaderStart();
+    }
+
+    /**
+     * Returns a list of LoaderManagers that have opted to retain their instance across
+     * configuration changes.
+     */
+    public SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+        return mHost.retainLoaderNonConfig();
+    }
+
+    /**
+     * Restores the saved state for all LoaderManagers. The given LoaderManager list are
+     * LoaderManager instances retained across configuration changes.
+     *
+     * @see #retainLoaderNonConfig()
+     */
+    public void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
+        mHost.restoreLoaderNonConfig(loaderManagers);
+    }
+
+    public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        mHost.dumpLoaders(prefix, fd, writer, args);
+    }
+}
diff --git a/v4/java/android/support/v4/app/FragmentHostCallbacks.java b/v4/java/android/support/v4/app/FragmentHostCallbacks.java
new file mode 100644
index 0000000..9b42a96
--- /dev/null
+++ b/v4/java/android/support/v4/app/FragmentHostCallbacks.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.v4.util.SimpleArrayMap;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Provides integration points with the host that is managing the {@link Fragment} lifecycle.
+ */
+public class FragmentHostCallbacks extends FragmentContainer {
+    private final Activity mActivity;
+    final Context mContext;
+    private final Handler mHandler;
+    final int mWindowAnimations;
+    final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+    private SimpleArrayMap<String, LoaderManager> mAllLoaderManagers;
+    private LoaderManagerImpl mLoaderManager;
+    private boolean mCheckedForLoaderManager;
+    private boolean mLoadersStarted;
+
+    public FragmentHostCallbacks(Context context, Handler handler, int windowAnimations) {
+        this(null /*activity*/, context, handler, windowAnimations);
+    }
+
+    FragmentHostCallbacks(FragmentActivity activity) {
+        this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
+    }
+
+    FragmentHostCallbacks(Activity activity, Context context, Handler handler,
+            int windowAnimations) {
+        mActivity = activity;
+        mContext = context;
+        mHandler = handler;
+        mWindowAnimations = windowAnimations;
+    }
+
+    /**
+     * Print internal state into the given stream.
+     *
+     * @param prefix Desired prefix to prepend at each line of output.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer The PrintWriter to which you should dump your state. This will be closed
+     *                  for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+    }
+
+    /**
+     * Return {@code true} if the fragment's state needs to be saved.
+     */
+    public boolean shouldSaveFragmentState(Fragment fragment) {
+        return true;
+    }
+
+    /**
+     * Return a {@link LayoutInflater}.
+     * See {@link Activity#getLayoutInflater()}.
+     */
+    public LayoutInflater getLayoutInflater() {
+        return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    /**
+     * Invalidates the activity's options menu.
+     * See {@link FragmentActivity#supportInvalidateOptionsMenu()}
+     */
+    public void supportInvalidateOptionsMenu() {
+    }
+
+    /**
+     * Starts a new {@link Activity} from the given fragment.
+     * See {@link FragmentActivity#startActivityForResult(Intent, int)}.
+     */
+    public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
+        if (requestCode != -1) {
+            throw new IllegalStateException(
+                    "Starting activity with a requestCode requires a FragmentActivity host");
+        }
+        mContext.startActivity(intent);
+    }
+
+    /**
+     * Return {@code true} if there are window animations.
+     */
+    public boolean hasWindowAnimations() {
+        return true;
+    }
+
+    /**
+     * Return the window animations.
+     */
+    public int getWindowAnimations() {
+        return mWindowAnimations;
+    }
+
+    @Nullable
+    @Override
+    public View findViewById(int id) {
+        return null;
+    }
+
+    @Override
+    public boolean hasView() {
+        return true;
+    }
+
+    Activity getActivity() {
+        return mActivity;
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    Handler getHandler() {
+        return mHandler;
+    }
+
+    FragmentManagerImpl getFragmentManagerImpl() {
+        return mFragmentManager;
+    }
+
+    LoaderManagerImpl getLoaderManagerImpl() {
+        if (mLoaderManager != null) {
+            return mLoaderManager;
+        }
+        mCheckedForLoaderManager = true;
+        mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
+        return mLoaderManager;
+    }
+
+    void inactivateFragment(String who) {
+        //Log.v(TAG, "invalidateSupportFragment: who=" + who);
+        if (mAllLoaderManagers != null) {
+            LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+            if (lm != null && !lm.mRetaining) {
+                lm.doDestroy();
+                mAllLoaderManagers.remove(who);
+            }
+        }
+    }
+
+    void onFragmentInflate(Fragment fragment, AttributeSet attrs, Bundle savedInstanceState) {
+        fragment.onInflate(mContext, attrs, savedInstanceState);
+    }
+
+    void onFragmentAttach(Fragment fragment) {
+        fragment.onAttach(mContext);
+    }
+
+    void doLoaderStart() {
+        if (mLoadersStarted) {
+            return;
+        }
+        mLoadersStarted = true;
+
+        if (mLoaderManager != null) {
+            mLoaderManager.doStart();
+        } else if (!mCheckedForLoaderManager) {
+            mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
+            // the returned loader manager may be a new one, so we have to start it
+            if ((mLoaderManager != null) && (!mLoaderManager.mStarted)) {
+                mLoaderManager.doStart();
+            }
+        }
+        mCheckedForLoaderManager = true;
+    }
+
+    // retain -- whether to stop the loader or retain it
+    void doLoaderStop(boolean retain) {
+        if (mLoaderManager == null) {
+            return;
+        }
+
+        if (!mLoadersStarted) {
+            return;
+        }
+        mLoadersStarted = false;
+
+        if (retain) {
+            mLoaderManager.doRetain();
+        } else {
+            mLoaderManager.doStop();
+        }
+    }
+
+    void doLoaderRetain() {
+        if (mLoaderManager == null) {
+            return;
+        }
+        mLoaderManager.doRetain();
+    }
+
+    void doLoaderDestroy() {
+        if (mLoaderManager == null) {
+            return;
+        }
+        mLoaderManager.doDestroy();
+    }
+
+    void reportLoaderStart() {
+        if (mAllLoaderManagers != null) {
+            final int N = mAllLoaderManagers.size();
+            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+            for (int i=N-1; i>=0; i--) {
+                loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+            }
+            for (int i=0; i<N; i++) {
+                LoaderManagerImpl lm = loaders[i];
+                lm.finishRetain();
+                lm.doReportStart();
+            }
+        }
+    }
+
+    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
+        if (mAllLoaderManagers == null) {
+            mAllLoaderManagers = new SimpleArrayMap<String, LoaderManager>();
+        }
+        LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+        if (lm == null) {
+            if (create) {
+                lm = new LoaderManagerImpl(who, this, started);
+                mAllLoaderManagers.put(who, lm);
+            }
+        } else {
+            lm.updateHostController(this);
+        }
+        return lm;
+    }
+
+    SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+        boolean retainLoaders = false;
+        if (mAllLoaderManagers != null) {
+            // prune out any loader managers that were already stopped and so
+            // have nothing useful to retain.
+            final int N = mAllLoaderManagers.size();
+            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+            for (int i=N-1; i>=0; i--) {
+                loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+            }
+            for (int i=0; i<N; i++) {
+                LoaderManagerImpl lm = loaders[i];
+                if (lm.mRetaining) {
+                    retainLoaders = true;
+                } else {
+                    lm.doDestroy();
+                    mAllLoaderManagers.remove(lm.mWho);
+                }
+            }
+        }
+
+        if (retainLoaders) {
+            return mAllLoaderManagers;
+        }
+        return null;
+    }
+
+    void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
+        mAllLoaderManagers = loaderManagers;
+    }
+
+    void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.print(prefix); writer.print("mLoadersStarted=");
+        writer.println(mLoadersStarted);
+        if (mLoaderManager != null) {
+            writer.print(prefix); writer.print("Loader Manager ");
+            writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
+            writer.println(":");
+            mLoaderManager.dump(prefix + "  ", fd, writer, args);
+        }
+    }
+}
diff --git a/v4/java/android/support/v4/app/FragmentManager.java b/v4/java/android/support/v4/app/FragmentManager.java
index 6d41d45..5739057 100644
--- a/v4/java/android/support/v4/app/FragmentManager.java
+++ b/v4/java/android/support/v4/app/FragmentManager.java
@@ -26,7 +26,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.support.annotation.IdRes;
-import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.v4.util.DebugUtils;
 import android.support.v4.util.LogWriter;
@@ -399,15 +398,6 @@
 }
 
 /**
- * Callbacks from FragmentManagerImpl to its container.
- */
-interface FragmentContainer {
-    @Nullable
-    public View findViewById(@IdRes int id);
-    public boolean hasView();
-}
-
-/**
  * Container for fragments associated with an activity.
  */
 final class FragmentManagerImpl extends FragmentManager implements LayoutInflaterFactory {
@@ -438,7 +428,8 @@
     ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
 
     int mCurState = Fragment.INITIALIZING;
-    FragmentActivity mActivity;
+    FragmentHostCallbacks mHost;
+    FragmentController mController;
     FragmentContainer mContainer;
     Fragment mParent;
     
@@ -464,9 +455,9 @@
         Log.e(TAG, "Activity state:");
         LogWriter logw = new LogWriter(TAG);
         PrintWriter pw = new PrintWriter(logw);
-        if (mActivity != null) {
+        if (mHost != null) {
             try {
-                mActivity.dump("  ", null, pw, new String[] { });
+                mHost.dump("  ", null, pw, new String[] { });
             } catch (Exception e) {
                 Log.e(TAG, "Failed dumping state", e);
             }
@@ -494,7 +485,7 @@
     public void popBackStack() {
         enqueueAction(new Runnable() {
             @Override public void run() {
-                popBackStackState(mActivity.mHandler, null, -1, 0);
+                popBackStackState(mHost.getHandler(), null, -1, 0);
             }
         }, false);
     }
@@ -503,14 +494,14 @@
     public boolean popBackStackImmediate() {
         checkStateLoss();
         executePendingTransactions();
-        return popBackStackState(mActivity.mHandler, null, -1, 0);
+        return popBackStackState(mHost.getHandler(), null, -1, 0);
     }
 
     @Override
     public void popBackStack(final String name, final int flags) {
         enqueueAction(new Runnable() {
             @Override public void run() {
-                popBackStackState(mActivity.mHandler, name, -1, flags);
+                popBackStackState(mHost.getHandler(), name, -1, flags);
             }
         }, false);
     }
@@ -519,7 +510,7 @@
     public boolean popBackStackImmediate(String name, int flags) {
         checkStateLoss();
         executePendingTransactions();
-        return popBackStackState(mActivity.mHandler, name, -1, flags);
+        return popBackStackState(mHost.getHandler(), name, -1, flags);
     }
 
     @Override
@@ -529,7 +520,7 @@
         }
         enqueueAction(new Runnable() {
             @Override public void run() {
-                popBackStackState(mActivity.mHandler, null, id, flags);
+                popBackStackState(mHost.getHandler(), null, id, flags);
             }
         }, false);
     }
@@ -541,7 +532,7 @@
         if (id < 0) {
             throw new IllegalArgumentException("Bad id: " + id);
         }
-        return popBackStackState(mActivity.mHandler, null, id, flags);
+        return popBackStackState(mHost.getHandler(), null, id, flags);
     }
 
     @Override
@@ -628,7 +619,7 @@
         if (mParent != null) {
             DebugUtils.buildShortClassTag(mParent, sb);
         } else {
-            DebugUtils.buildShortClassTag(mActivity, sb);
+            DebugUtils.buildShortClassTag(mHost, sb);
         }
         sb.append("}}");
         return sb.toString();
@@ -725,7 +716,7 @@
         }
 
         writer.print(prefix); writer.println("FragmentManager misc state:");
-        writer.print(prefix); writer.print("  mActivity="); writer.println(mActivity);
+        writer.print(prefix); writer.print("  mHost="); writer.println(mHost);
         writer.print(prefix); writer.print("  mContainer="); writer.println(mContainer);
         if (mParent != null) {
             writer.print(prefix); writer.print("  mParent="); writer.println(mParent);
@@ -785,7 +776,7 @@
         }
         
         if (fragment.mNextAnim != 0) {
-            Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim);
+            Animation anim = AnimationUtils.loadAnimation(mHost.getContext(), fragment.mNextAnim);
             if (anim != null) {
                 return anim;
             }
@@ -802,21 +793,21 @@
         
         switch (styleIndex) {
             case ANIM_STYLE_OPEN_ENTER:
-                return makeOpenCloseAnimation(mActivity, 1.125f, 1.0f, 0, 1);
+                return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1);
             case ANIM_STYLE_OPEN_EXIT:
-                return makeOpenCloseAnimation(mActivity, 1.0f, .975f, 1, 0);
+                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0);
             case ANIM_STYLE_CLOSE_ENTER:
-                return makeOpenCloseAnimation(mActivity, .975f, 1.0f, 0, 1);
+                return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1);
             case ANIM_STYLE_CLOSE_EXIT:
-                return makeOpenCloseAnimation(mActivity, 1.0f, 1.075f, 1, 0);
+                return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0);
             case ANIM_STYLE_FADE_ENTER:
-                return makeFadeAnimation(mActivity, 0, 1);
+                return makeFadeAnimation(mHost.getContext(), 0, 1);
             case ANIM_STYLE_FADE_EXIT:
-                return makeFadeAnimation(mActivity, 1, 0);
+                return makeFadeAnimation(mHost.getContext(), 1, 0);
         }
         
-        if (transitionStyle == 0 && mActivity.getWindow() != null) {
-            transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
+        if (transitionStyle == 0 && mHost.hasWindowAnimations()) {
+            transitionStyle = mHost.getWindowAnimations();
         }
         if (transitionStyle == 0) {
             return null;
@@ -881,7 +872,7 @@
                 case Fragment.INITIALIZING:
                     if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                     if (f.mSavedFragmentState != null) {
-                        f.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+                        f.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                         f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
                                 FragmentManagerImpl.VIEW_STATE_TAG);
                         f.mTarget = getFragment(f.mSavedFragmentState,
@@ -899,18 +890,18 @@
                             }
                         }
                     }
-                    f.mActivity = mActivity;
+                    f.mHost = mHost;
                     f.mParentFragment = mParent;
                     f.mFragmentManager = mParent != null
-                            ? mParent.mChildFragmentManager : mActivity.mFragments;
+                            ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                     f.mCalled = false;
-                    f.onAttach(mActivity);
+                    mHost.onFragmentAttach(f);
                     if (!f.mCalled) {
                         throw new SuperNotCalledException("Fragment " + f
                                 + " did not call through to super.onAttach()");
                     }
                     if (f.mParentFragment == null) {
-                        mActivity.onAttachFragment(f);
+                        mHost.onFragmentAttach(f);
                     }
 
                     if (!f.mRetaining) {
@@ -1021,7 +1012,7 @@
                         if (f.mView != null) {
                             // Need to save the current view state if not
                             // done already.
-                            if (!mActivity.isFinishing() && f.mSavedViewState == null) {
+                            if (mHost.shouldSaveFragmentState(f) && f.mSavedViewState == null) {
                                 saveFragmentViewState(f);
                             }
                         }
@@ -1098,7 +1089,7 @@
                                 if (!f.mRetaining) {
                                     makeInactive(f);
                                 } else {
-                                    f.mActivity = null;
+                                    f.mHost = null;
                                     f.mParentFragment = null;
                                     f.mFragmentManager = null;
                                     f.mChildFragmentManager = null;
@@ -1121,8 +1112,8 @@
     }
     
     void moveToState(int newState, int transit, int transitStyle, boolean always) {
-        if (mActivity == null && newState != Fragment.INITIALIZING) {
-            throw new IllegalStateException("No activity");
+        if (mHost == null && newState != Fragment.INITIALIZING) {
+            throw new IllegalStateException("No host");
         }
 
         if (!always && mCurState == newState) {
@@ -1146,8 +1137,8 @@
                 startPendingDeferredFragments();
             }
 
-            if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
-                mActivity.supportInvalidateOptionsMenu();
+            if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
+                mHost.supportInvalidateOptionsMenu();
                 mNeedMenuInvalidate = false;
             }
         }
@@ -1194,7 +1185,7 @@
             mAvailIndices = new ArrayList<Integer>();
         }
         mAvailIndices.add(f.mIndex);
-        mActivity.invalidateSupportFragment(f.mWho);
+        mHost.inactivateFragment(f.mWho);
         f.initState();
     }
     
@@ -1395,7 +1386,7 @@
             checkStateLoss();
         }
         synchronized (this) {
-            if (mDestroyed || mActivity == null) {
+            if (mDestroyed || mHost == null) {
                 throw new IllegalStateException("Activity has been destroyed");
             }
             if (mPendingActions == null) {
@@ -1403,8 +1394,8 @@
             }
             mPendingActions.add(action);
             if (mPendingActions.size() == 1) {
-                mActivity.mHandler.removeCallbacks(mExecCommit);
-                mActivity.mHandler.post(mExecCommit);
+                mHost.getHandler().removeCallbacks(mExecCommit);
+                mHost.getHandler().post(mExecCommit);
             }
         }
     }
@@ -1473,7 +1464,7 @@
             throw new IllegalStateException("Recursive entry to executePendingTransactions");
         }
         
-        if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
+        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
             throw new IllegalStateException("Must be called from main thread of process");
         }
 
@@ -1493,7 +1484,7 @@
                 }
                 mPendingActions.toArray(mTmpActions);
                 mPendingActions.clear();
-                mActivity.mHandler.removeCallbacks(mExecCommit);
+                mHost.getHandler().removeCallbacks(mExecCommit);
             }
             
             mExecutingActions = true;
@@ -1537,6 +1528,7 @@
         reportBackStackChanged();
     }
     
+    @SuppressWarnings("unused")
     boolean popBackStackState(Handler handler, String name, int id, int flags) {
         if (mBackStack == null) {
             return false;
@@ -1780,7 +1772,7 @@
             if (N > 0) {
                 backStack = new BackStackState[N];
                 for (int i=0; i<N; i++) {
-                    backStack[i] = new BackStackState(this, mBackStack.get(i));
+                    backStack[i] = new BackStackState(mBackStack.get(i));
                     if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                             + ": " + mBackStack.get(i));
                 }
@@ -1815,7 +1807,7 @@
                 f.mAdded = false;
                 f.mTarget = null;
                 if (fs.mSavedFragmentState != null) {
-                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+                    fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                     f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                             FragmentManagerImpl.VIEW_STATE_TAG);
                     f.mSavedFragmentState = fs.mSavedFragmentState;
@@ -1832,7 +1824,7 @@
         for (int i=0; i<fms.mActive.length; i++) {
             FragmentState fs = fms.mActive[i];
             if (fs != null) {
-                Fragment f = fs.instantiate(mActivity, mParent);
+                Fragment f = fs.instantiate(mHost, mParent);
                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                 mActive.add(f);
                 // Now that the fragment is instantiated (or came from being
@@ -1906,11 +1898,11 @@
             mBackStack = null;
         }
     }
-    
-    public void attachActivity(FragmentActivity activity,
+
+    public void attachController(FragmentHostCallbacks host,
             FragmentContainer container, Fragment parent) {
-        if (mActivity != null) throw new IllegalStateException("Already attached");
-        mActivity = activity;
+        if (mHost != null) throw new IllegalStateException("Already attached");
+        mHost = host;
         mContainer = container;
         mParent = parent;
     }
@@ -1964,7 +1956,7 @@
         mDestroyed = true;
         execPendingActions();
         moveToState(Fragment.INITIALIZING, false);
-        mActivity = null;
+        mHost = null;
         mContainer = null;
         mParent = null;
     }
@@ -2132,7 +2124,7 @@
         String tag = a.getString(FragmentTag.Fragment_tag);
         a.recycle();
 
-        if (!Fragment.isSupportFragmentClass(mActivity, fname)) {
+        if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) {
             // Invalid support lib fragment; let the device's framework handle it.
             // This will allow android.app.Fragments to do the right thing.
             return null;
@@ -2166,7 +2158,7 @@
             fragment.mTag = tag;
             fragment.mInLayout = true;
             fragment.mFragmentManager = this;
-            fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+            mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
             addFragment(fragment, true);
 
         } else if (fragment.mInLayout) {
@@ -2184,7 +2176,7 @@
             // from last saved state), then give it the attributes to
             // initialize itself.
             if (!fragment.mRetaining) {
-                fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+                mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
             }
         }
 
diff --git a/v4/java/android/support/v4/app/LoaderManager.java b/v4/java/android/support/v4/app/LoaderManager.java
index e57d01c..e67cc4b 100644
--- a/v4/java/android/support/v4/app/LoaderManager.java
+++ b/v4/java/android/support/v4/app/LoaderManager.java
@@ -184,6 +184,9 @@
     public boolean hasRunningLoaders() { return false; }
 }
 
+/**
+ * @hide
+ */
 class LoaderManagerImpl extends LoaderManager {
     static final String TAG = "LoaderManager";
     static boolean DEBUG = false;
@@ -201,12 +204,12 @@
 
     final String mWho;
 
-    FragmentActivity mActivity;
     boolean mStarted;
     boolean mRetaining;
     boolean mRetainingStarted;
     
     boolean mCreatingLoader;
+    private FragmentHostCallbacks mHost;
 
     final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
             Loader.OnLoadCanceledListener<Object> {
@@ -217,8 +220,11 @@
         boolean mHaveData;
         boolean mDeliveredData;
         Object mData;
+        @SuppressWarnings("hiding")
         boolean mStarted;
+        @SuppressWarnings("hiding")
         boolean mRetaining;
+        @SuppressWarnings("hiding")
         boolean mRetainingStarted;
         boolean mReportNextStart;
         boolean mDestroyed;
@@ -343,15 +349,15 @@
             if (mCallbacks != null && mLoader != null && mHaveData && needReset) {
                 if (DEBUG) Log.v(TAG, "  Reseting: " + this);
                 String lastBecause = null;
-                if (mActivity != null) {
-                    lastBecause = mActivity.mFragments.mNoTransactionsBecause;
-                    mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+                if (mHost != null) {
+                    lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+                    mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset";
                 }
                 try {
                     mCallbacks.onLoaderReset(mLoader);
                 } finally {
-                    if (mActivity != null) {
-                        mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+                    if (mHost != null) {
+                        mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
                     }
                 }
             }
@@ -452,25 +458,25 @@
                 mInactiveLoaders.remove(mId);
             }
 
-            if (mActivity != null && !hasRunningLoaders()) {
-                mActivity.mFragments.startPendingDeferredFragments();
+            if (mHost != null && !hasRunningLoaders()) {
+                mHost.mFragmentManager.startPendingDeferredFragments();
             }
         }
 
         void callOnLoadFinished(Loader<Object> loader, Object data) {
             if (mCallbacks != null) {
                 String lastBecause = null;
-                if (mActivity != null) {
-                    lastBecause = mActivity.mFragments.mNoTransactionsBecause;
-                    mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
+                if (mHost != null) {
+                    lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+                    mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
                 }
                 try {
                     if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "
                             + loader.dataToString(data));
                     mCallbacks.onLoadFinished(loader, data);
                 } finally {
-                    if (mActivity != null) {
-                        mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+                    if (mHost != null) {
+                        mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
                     }
                 }
                 mDeliveredData = true;
@@ -517,21 +523,21 @@
         }
     }
     
-    LoaderManagerImpl(String who, FragmentActivity activity, boolean started) {
+    LoaderManagerImpl(String who, FragmentHostCallbacks host, boolean started) {
         mWho = who;
-        mActivity = activity;
+        mHost = host;
         mStarted = started;
     }
     
-    void updateActivity(FragmentActivity activity) {
-        mActivity = activity;
+    void updateHostController(FragmentHostCallbacks host) {
+        mHost = host;
     }
     
     private LoaderInfo createLoader(int id, Bundle args,
             LoaderManager.LoaderCallbacks<Object> callback) {
-        LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+        LoaderInfo info = new LoaderInfo(id, args,  callback);
         Loader<Object> loader = callback.onCreateLoader(id, args);
-        info.mLoader = (Loader<Object>)loader;
+        info.mLoader = loader;
         return info;
     }
     
@@ -576,7 +582,7 @@
      * @param id A unique (to this LoaderManager instance) identifier under
      * which to manage the new Loader.
      * @param args Optional arguments that will be propagated to
-     * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
+     * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
      * @param callback Interface implementing management of this Loader.  Required.
      * Its onCreateLoader() method will be called while inside of the function to
      * instantiate the Loader object.
@@ -626,7 +632,7 @@
      * @param id A unique (to this LoaderManager instance) identifier under
      * which to manage the new Loader.
      * @param args Optional arguments that will be propagated to
-     * {@link LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
+     * {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader(int, Bundle) LoaderCallbacks.onCreateLoader()}.
      * @param callback Interface implementing management of this Loader.  Required.
      * Its onCreateLoader() method will be called while inside of the function to
      * instantiate the Loader object.
@@ -717,8 +723,8 @@
             mInactiveLoaders.removeAt(idx);
             info.destroy();
         }
-        if (mActivity != null && !hasRunningLoaders()) {
-            mActivity.mFragments.startPendingDeferredFragments();
+        if (mHost != null && !hasRunningLoaders()) {
+            mHost.mFragmentManager.startPendingDeferredFragments();
         }
     }
 
@@ -836,7 +842,7 @@
         sb.append("LoaderManager{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(" in ");
-        DebugUtils.buildShortClassTag(mActivity, sb);
+        DebugUtils.buildShortClassTag(mHost, sb);
         sb.append("}}");
         return sb.toString();
     }