Implement issue # 3255887 could CursorLoader offer...

...to throttle contentobserver-based requeries

Why yes, I guess it could.

This also reworks AsyncTaskLoader to not generate multiple
concurrent tasks if it is getting change notifications before
the last background task is complete.

And removes some of the old APIs that had been deprecated but
need to be gone for final release.

And fixes a few little problems with applying the wrong theme
in system code.

Change-Id: Ic7a665b666d0fb9d348e5f23595532191065884f
diff --git a/api/current.xml b/api/current.xml
index 53f7f1e..7e81eb7 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -22817,32 +22817,6 @@
 <parameter name="args" type="java.lang.String[]">
 </parameter>
 </method>
-<method name="findFragmentById"
- return="android.app.Fragment"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-<method name="findFragmentByTag"
- return="android.app.Fragment"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="tag" type="java.lang.String">
-</parameter>
-</method>
 <method name="findViewById"
  return="android.view.View"
  abstract="false"
@@ -24065,17 +24039,6 @@
 <parameter name="view" type="android.view.View">
 </parameter>
 </method>
-<method name="openFragmentTransaction"
- return="android.app.FragmentTransaction"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-</method>
 <method name="openOptionsMenu"
  return="void"
  abstract="false"
@@ -43623,6 +43586,30 @@
 <parameter name="data" type="D">
 </parameter>
 </method>
+<method name="onLoadInBackground"
+ return="D"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="setUpdateThrottle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="delayMS" type="long">
+</parameter>
+</method>
 <method name="waitForLoader"
  return="void"
  abstract="false"
@@ -50212,21 +50199,6 @@
 <parameter name="cursor" type="android.database.Cursor">
 </parameter>
 </method>
-<method name="registerContentObserver"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cursor" type="android.database.Cursor">
-</parameter>
-<parameter name="observer" type="android.database.ContentObserver">
-</parameter>
-</method>
 <method name="setProjection"
  return="void"
  abstract="false"
@@ -55453,7 +55425,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -55578,7 +55550,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -55600,7 +55572,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -68017,6 +67989,21 @@
 <parameter name="selectionArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="concatenateWhere"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="a" type="java.lang.String">
+</parameter>
+<parameter name="b" type="java.lang.String">
+</parameter>
+</method>
 <method name="createDbFromSqlStatements"
  return="void"
  abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 938c47d..ec2f771 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -73,13 +73,11 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
-import android.widget.FrameLayout;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 
 /**
  * An activity is a single, focused thing that the user can do.  Almost all
@@ -1579,16 +1577,6 @@
         return mFragments;
     }
 
-    /**
-     * Start a series of edit operations on the Fragments associated with
-     * this activity.
-     * @deprecated use {@link #getFragmentManager}.
-     */
-    @Deprecated
-    public FragmentTransaction openFragmentTransaction() {
-        return mFragments.openTransaction();
-    }
-    
     void invalidateFragmentIndex(int index) {
         //Log.v(TAG, "invalidateFragmentIndex: index=" + index);
         if (mAllLoaderManagers != null) {
@@ -1770,30 +1758,6 @@
     }
     
     /**
-     * Finds a fragment that was identified by the given id either when inflated
-     * from XML or as the container ID when added in a transaction.  This only
-     * returns fragments that are currently added to the activity's content.
-     * @return The fragment if found or null otherwise.
-     * @deprecated use {@link #getFragmentManager}.
-     */
-    @Deprecated
-    public Fragment findFragmentById(int id) {
-        return mFragments.findFragmentById(id);
-    }
-    
-    /**
-     * Finds a fragment that was identified by the given tag either when inflated
-     * from XML or as supplied when added in a transaction.  This only
-     * returns fragments that are currently added to the activity's content.
-     * @return The fragment if found or null otherwise.
-     * @deprecated use {@link #getFragmentManager}.
-     */
-    @Deprecated
-    public Fragment findFragmentByTag(String tag) {
-        return mFragments.findFragmentByTag(tag);
-    }
-    
-    /**
      * Set the activity content from a layout resource.  The resource will be
      * inflated, adding all top-level views to the activity.
      * 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 72f7286..16d3a7f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -485,6 +485,11 @@
     }
 
     @Override
+    public int getThemeResId() {
+        return mThemeResource;
+    }
+
+    @Override
     public Resources.Theme getTheme() {
         if (mTheme == null) {
             if (mThemeResource == 0) {
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index bcd46d9..72a455e 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -211,7 +211,7 @@
      */
     @Deprecated
     public void show(Activity activity, String tag) {
-        FragmentTransaction ft = activity.openFragmentTransaction();
+        FragmentTransaction ft = activity.getFragmentManager().openTransaction();
         ft.add(this, tag);
         ft.commit();
     }
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 5aec348..1b8debc 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -24,8 +24,8 @@
      * @param fragment The fragment to be added.  This fragment must not already
      * be added to the activity.
      * @param tag Optional tag name for the fragment, to later retrieve the
-     * fragment with {@link Activity#findFragmentByTag(String)
-     * Activity.findFragmentByTag(String)}.
+     * fragment with {@link FragmentManager#findFragmentByTag(String)
+     * FragmentManager.findFragmentByTag(String)}.
      * 
      * @return Returns the same FragmentTransaction instance.
      */
@@ -47,8 +47,8 @@
      * to be replaced.
      * @param fragment The new fragment to place in the container.
      * @param tag Optional tag name for the fragment, to later retrieve the
-     * fragment with {@link Activity#findFragmentByTag(String)
-     * Activity.findFragmentByTag(String)}.
+     * fragment with {@link FragmentManager#findFragmentByTag(String)
+     * FragmentManager.findFragmentByTag(String)}.
      * 
      * @return Returns the same FragmentTransaction instance.
      */
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index 01a2912..ec4e578 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -17,8 +17,13 @@
 package android.content;
 
 import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Log;
+import android.util.TimeUtils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -30,14 +35,15 @@
 
     private static final String TAG = "AsyncTaskLoader";
 
-    final class LoadTask extends AsyncTask<Void, Void, D> {
+    final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {
 
-        private D result;
+        D result;
+        boolean waiting;
 
         /* Runs on a worker thread */
         @Override
         protected D doInBackground(Void... params) {
-            result = AsyncTaskLoader.this.loadInBackground();
+            result = AsyncTaskLoader.this.onLoadInBackground();
             return result;
         }
 
@@ -49,38 +55,91 @@
 
         @Override
         protected void onCancelled() {
-            AsyncTaskLoader.this.onCancelled(result);
+            AsyncTaskLoader.this.dispatchOnCancelled(this, result);
+        }
+
+        @Override
+        public void run() {
+            waiting = false;
+            AsyncTaskLoader.this.executePendingTask();
         }
     }
 
     volatile LoadTask mTask;
+    volatile LoadTask mCancellingTask;
+
+    long mUpdateThrottle;
+    long mLastLoadCompleteTime = -10000;
+    Handler mHandler;
 
     public AsyncTaskLoader(Context context) {
         super(context);
     }
 
+    /**
+     * Set amount to throttle updates by.  This is the minimum time from
+     * when the last {@link #onLoadInBackground()} call has completed until
+     * a new load is scheduled.
+     *
+     * @param delayMS Amount of delay, in milliseconds.
+     */
+    public void setUpdateThrottle(long delayMS) {
+        mUpdateThrottle = delayMS;
+        if (delayMS != 0) {
+            mHandler = new Handler();
+        }
+    }
+
     @Override
     protected void onForceLoad() {
         super.onForceLoad();
         cancelLoad();
         mTask = new LoadTask();
-        mTask.execute((Void[]) null);
+        executePendingTask();
     }
 
     /**
      * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
-     * for more info.
+     * for more info.  Must be called on the main thread of the process.
      *
-     * @return <tt>false</tt> if the task could not be canceled,
+     * <p>Cancelling is not an immediate operation, since the load is performed
+     * in a background thread.  If there is currently a load in progress, this
+     * method requests that the load be cancelled, and notes this is the case;
+     * once the background thread has completed its work its remaining state
+     * will be cleared.  If another load request comes in during this time,
+     * it will be held until the cancelled load is complete.
+     *
+     * @return Returns <tt>false</tt> if the task could not be cancelled,
      *         typically because it has already completed normally, or
-     *         because {@link #startLoading()} hasn't been called, and
-     *         <tt>true</tt> otherwise
+     *         because {@link #startLoading()} hasn't been called; returns
+     *         <tt>true</tt> otherwise.
      */
     public boolean cancelLoad() {
         if (mTask != null) {
-            boolean cancelled = mTask.cancel(false);
-            mTask = null;
-            return cancelled;
+            if (mCancellingTask != null) {
+                // There was a pending task already waiting for a previous
+                // one being canceled; just drop it.
+                if (mTask.waiting) {
+                    mTask.waiting = false;
+                    mHandler.removeCallbacks(mTask);
+                }
+                mTask = null;
+                return false;
+            } else if (mTask.waiting) {
+                // There is a task, but it is waiting for the time it should
+                // execute.  We can just toss it.
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+                mTask = null;
+                return false;
+            } else {
+                boolean cancelled = mTask.cancel(false);
+                if (cancelled) {
+                    mCancellingTask = mTask;
+                }
+                mTask = null;
+                return cancelled;
+            }
         }
         return false;
     }
@@ -92,32 +151,67 @@
     public void onCancelled(D data) {
     }
 
+    void executePendingTask() {
+        if (mCancellingTask == null && mTask != null) {
+            if (mTask.waiting) {
+                mTask.waiting = false;
+                mHandler.removeCallbacks(mTask);
+            }
+            if (mUpdateThrottle > 0) {
+                long now = SystemClock.uptimeMillis();
+                if (now < (mLastLoadCompleteTime+mUpdateThrottle)) {
+                    // Not yet time to do another load.
+                    mTask.waiting = true;
+                    mHandler.postAtTime(mTask, mLastLoadCompleteTime+mUpdateThrottle);
+                    return;
+                }
+            }
+            mTask.execute((Void[]) null);
+        }
+    }
+
+    void dispatchOnCancelled(LoadTask task, D data) {
+        onCancelled(data);
+        if (mCancellingTask == task) {
+            mLastLoadCompleteTime = SystemClock.uptimeMillis();
+            mCancellingTask = null;
+            executePendingTask();
+        }
+    }
+
     void dispatchOnLoadComplete(LoadTask task, D data) {
         if (mTask != task) {
-            onCancelled(data);
+            dispatchOnCancelled(task, data);
         } else {
+            mLastLoadCompleteTime = SystemClock.uptimeMillis();
             mTask = null;
             deliverResult(data);
         }
     }
 
     /**
-     * Called on a worker thread to perform the actual load. Implementations should not deliver the
-     * results directly, but should return them from this method, which will eventually end up
-     * calling deliverResult on the UI thread. If implementations need to process
-     * the results on the UI thread they may override deliverResult and do so
-     * there.
-     *
-     * @return the result of the load
      */
     public abstract D loadInBackground();
 
     /**
+     * Called on a worker thread to perform the actual load. Implementations should not deliver the
+     * result directly, but should return them from this method, which will eventually end up
+     * calling {@link #deliverResult} on the UI thread. If implementations need to process
+     * the results on the UI thread they may override {@link #deliverResult} and do so
+     * there.
+     *
+     * @return Implementations must return the result of their load operation.
+     */
+    protected D onLoadInBackground() {
+        return loadInBackground();
+    }
+
+    /**
      * Locks the current thread until the loader completes the current load
      * operation. Returns immediately if there is no load operation running.
      * Should not be called from the UI thread.
      * <p>
-     * Used for testing.
+     * Use for testing only.  <b>Never</b> call this from a UI thread.
      */
     public void waitForLoader() {
         LoadTask task = mTask;
@@ -132,4 +226,25 @@
             }
         }
     }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        if (mTask != null) {
+            writer.print(prefix); writer.print("mTask="); writer.print(mTask);
+                    writer.print(" waiting="); writer.println(mTask.waiting);
+        }
+        if (mCancellingTask != null) {
+            writer.print(prefix); writer.print("mCancellingTask="); writer.print(mCancellingTask);
+                    writer.print(" waiting="); writer.println(mCancellingTask.waiting);
+        }
+        if (mUpdateThrottle != 0) {
+            writer.print(prefix); writer.print("mUpdateThrottle=");
+                    TimeUtils.formatDuration(mUpdateThrottle, writer);
+                    writer.print(" mLastLoadCompleteTime=");
+                    TimeUtils.formatDuration(mLastLoadCompleteTime,
+                            SystemClock.uptimeMillis(), writer);
+                    writer.println();
+        }
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b128d31..227df21 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -227,6 +227,12 @@
      */
     public abstract void setTheme(int resid);
 
+    /** @hide Needed for some internal implementation...  not public because
+     * you can't assume this actually means anything. */
+    public int getThemeResId() {
+        return 0;
+    }
+
     /**
      * Return the Theme object associated with this Context.
      */
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f8928e4..545144e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -106,6 +106,12 @@
         mBase.setTheme(resid);
     }
 
+    /** @hide */
+    @Override
+    public int getThemeResId() {
+        return mBase.getThemeResId();
+    }
+
     @Override
     public Resources.Theme getTheme() {
         return mBase.getTheme();
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 8ab0973..38ebaf2 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -55,7 +55,7 @@
      * Registers an observer to get notifications from the content provider
      * when the cursor needs to be refreshed.
      */
-    public void registerContentObserver(Cursor cursor, ContentObserver observer) {
+    void registerContentObserver(Cursor cursor, ContentObserver observer) {
         cursor.registerContentObserver(mObserver);
     }
 
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index ef81fe4..d63fe69 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -28,6 +28,13 @@
  * they should monitor the source of their data and deliver new results when the contents
  * change.
  *
+ * <p><b>Note on threading:</b> Clients of loaders should as a rule perform
+ * any calls on to a Loader from the main thread of their process (that is,
+ * the thread the Activity callbacks and other things occur on).  Subclasses
+ * of Loader (such as {@link AsyncTaskLoader}) will often perform their work
+ * in a separate thread, but when delivering their results this too should
+ * be done on the main thread.</p>
+ *
  * <p>Subclasses generally must implement at least {@link #onStartLoading()},
  * {@link #onStopLoading()}, {@link #onForceLoad()}, and {@link #onReset()}.
  *
@@ -80,7 +87,7 @@
     /**
      * Sends the result of the load to the registered listener. Should only be called by subclasses.
      *
-     * Must be called from the UI thread.
+     * Must be called from the process's main thread.
      *
      * @param data the result of the load
      */
@@ -105,10 +112,11 @@
     }
 
     /**
-     * Registers a class that will receive callbacks when a load is complete. The callbacks will
-     * be called on the UI thread so it's safe to pass the results to widgets.
+     * Registers a class that will receive callbacks when a load is complete.
+     * The callback will be called on the process's main thread so it's safe to
+     * pass the results to widgets.
      *
-     * Must be called from the UI thread
+     * <p>Must be called from the process's main thread.
      */
     public void registerListener(int id, OnLoadCompleteListener<D> listener) {
         if (mListener != null) {
@@ -119,7 +127,9 @@
     }
 
     /**
-     * Must be called from the UI thread
+     * Remove a listener that was previously added with {@link #registerListener}.
+     *
+     * Must be called from the process's main thread.
      */
     public void unregisterListener(OnLoadCompleteListener<D> listener) {
         if (mListener == null) {
@@ -150,17 +160,19 @@
     }
 
     /**
-     * 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.
+     * Starts an asynchronous load of the Loader's data. When the result
+     * is ready the callbacks will be called on the process's main 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>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.
+     * <p>Must be called from the process's main thread.
      */
     public final void startLoading() {
         mStarted = true;
@@ -182,14 +194,15 @@
      * implementation's {@link #onForceLoad()}.  You generally should only call this
      * when the loader is started -- that is, {@link #isStarted()} returns true.
      *
-     * <p>Must be called from the UI thread.
+     * <p>Must be called from the process's main thread.
      */
-    public final void forceLoad() {
+    public void forceLoad() {
         onForceLoad();
     }
 
     /**
      * Subclasses must implement this to take care of requests to {@link #forceLoad()}.
+     * This will always be called from the process's main thread.
      */
     protected void onForceLoad() {
     }
@@ -206,9 +219,9 @@
      * {@link #isStarted()} will return the correct
      * value, and then calls the implementation's {@link #onStopLoading()}.
      *
-     * <p>Must be called from the UI thread.
+     * <p>Must be called from the process's main thread.
      */
-    public final void stopLoading() {
+    public void stopLoading() {
         mStarted = false;
         onStopLoading();
     }
@@ -217,6 +230,7 @@
      * 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()}.
+     * This will always be called from the process's main thread.
      */
     protected void onStopLoading() {
     }
@@ -231,9 +245,9 @@
      * {@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.
+     * <p>Must be called from the process's main thread.
      */
-    public final void reset() {
+    public void reset() {
         onReset();
         mReset = true;
         mStarted = false;
@@ -244,6 +258,7 @@
      * 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()}.
+     * This will always be called from the process's main thread.
      */
     protected void onReset() {
     }
@@ -265,7 +280,7 @@
      * if so, it simply calls {@link #forceLoad()}; otherwise, it sets a flag
      * so that {@link #takeContentChanged()} returns true.
      *
-     * <p>Must be called from the UI thread.
+     * <p>Must be called from the process's main thread.
      */
     public void onContentChanged() {
         if (mStarted) {
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 70a7fb6..f428aad 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -311,7 +311,6 @@
 
     /**
      * Concatenates two SQL WHERE clauses, handling empty or null values.
-     * @hide
      */
     public static String concatenateWhere(String a, String b) {
         if (TextUtils.isEmpty(a)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 873674d2..cec99e9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3244,8 +3244,8 @@
 
         /**
          * Minimum percentage of free storage on the device that is used to determine if
-         * the device is running low on storage.
-         * Say this value is set to 10, the device is considered running low on storage
+         * the device is running low on storage.  The default is 10.
+         * <p>Say this value is set to 10, the device is considered running low on storage
          * if 90% or more of the device storage is filled up.
          * @hide
          */
@@ -3253,6 +3253,16 @@
                 "sys_storage_threshold_percentage";
 
         /**
+         * Maximum byte size of the low storage threshold.  This is to ensure
+         * that {@link #SYS_STORAGE_THRESHOLD_PERCENTAGE} does not result in
+         * an overly large threshold for large storage devices.  Currently this
+         * must be less than 2GB.  This default is 500MB.
+         * @hide
+         */
+        public static final String SYS_STORAGE_THRESHOLD_MAX_BYTES =
+                "sys_storage_threshold_max_bytes";
+
+        /**
          * Minimum bytes of free storage on the device before the data
          * partition is considered full. By default, 1 MB is reserved
          * to avoid system-wide SQLite disk full exceptions.
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index ae938e7..be4152b 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -51,6 +51,12 @@
         initializeTheme();
     }
     
+    /** @hide */
+    @Override
+    public int getThemeResId() {
+        return mThemeResource;
+    }
+
     @Override public Resources.Theme getTheme() {
         if (mTheme != null) {
             return mTheme;
diff --git a/docs/html/guide/topics/fragments/index.jd b/docs/html/guide/topics/fragments/index.jd
index 766146e..d07daf4 100644
--- a/docs/html/guide/topics/fragments/index.jd
+++ b/docs/html/guide/topics/fragments/index.jd
@@ -277,13 +277,13 @@
 which to place the fragment.</p>
   <p>To make any fragment transactions in your activity (such as add, remove, or replace a
 fragment), you must use APIs from {@link android.app.FragmentTransaction}. You can get an instance
-of {@link android.app.FragmentTransaction} from your {@link android.app.Activity} using {@link
-android.app.Activity#openFragmentTransaction()}. You can then add a fragment using the {@link
+of {@link android.app.FragmentTransaction} from your {@link android.app.FragmentManager} using {@link
+android.app.FragmentManager#openTransaction()}. You can then add a fragment using the {@link
 android.app.FragmentTransaction#add add()} method, specifying the fragment to add and the view in
 which to insert it. For example:</p>
 <pre>
 MyFragment fragment = new MyFragment();
-openFragmentTransaction().add(R.id.fragment_container, fragment).commit();
+getFragmentManager().openTransaction().add(R.id.fragment_container, fragment).commit();
 </pre>
   <p>The first argument passed to {@link android.app.FragmentTransaction#add add()}
 is the {@link android.view.ViewGroup} in which the fragment should be placed, specified by
@@ -372,8 +372,8 @@
 operations on a fragment as the user interacts with the activity, alowing for more rich user
 experiences without changing activities. In order to perform these operations, you must use {@link
 android.app.FragmentTransaction} to perform fragment "transactions." You can acquire {@link
-android.app.FragmentTransaction} from your activity with {@link
-android.app.Activity#openFragmentTransaction}.</p>
+android.app.FragmentTransaction} from your FragmentManager with {@link
+android.app.FragmentManager#openTransaction}.</p>
 
 <p>Common transactions you can perform with fragments include:</p>
 
@@ -397,7 +397,7 @@
 <pre>
 // Create new fragment
 Fragment newFragment = new MyFragment();
-FragmentTransaction ft = openFragmentTransaction();
+FragmentTransaction ft = getFragmentManager().openTransaction();
 // Replace and add to back stack
 ft.replace(R.id.myfragment, newFragment);
 ft.addToBackStack(null);
@@ -519,7 +519,7 @@
 
 <p>Likewise, your activity can call public methods in the fragment when you have a reference to the
 {@link android.app.Fragment}. You can acquire a reference to the fragment with {@link
-android.app.Activity#findFragmentById findFragmentById()} and cast it to your implementation of
+android.app.FragmentManager#findFragmentById findFragmentById()} and cast it to your implementation of
 {@link android.app.Fragment}. For example:</p>
 
 <pre>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 5ad0edb..3e8318e 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -34,6 +34,7 @@
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
@@ -1017,23 +1018,16 @@
         
         try {
             Context context = mContext;
-            boolean setTheme = false;
             //Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="
             //        + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));
-            if (theme != 0 || labelRes != 0) {
+            if (theme != context.getThemeResId() || labelRes != 0) {
                 try {
                     context = context.createPackageContext(packageName, 0);
-                    if (theme != 0) {
-                        context.setTheme(theme);
-                        setTheme = true;
-                    }
+                    context.setTheme(theme);
                 } catch (PackageManager.NameNotFoundException e) {
                     // Ignore
                 }
             }
-            if (!setTheme) {
-                context.setTheme(com.android.internal.R.style.Theme);
-            }
             
             Window win = PolicyManager.makeNewWindow(context);
             if (win.getWindowStyle().getBoolean(
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index 0b1a4a3..0fba7c3 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -16,7 +16,6 @@
 
 package com.android.server;
 
-import com.android.server.am.ActivityManagerService;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -38,7 +37,6 @@
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Slog;
-import android.provider.Settings;
 
 /**
  * This class implements a service to monitor the amount of disk
@@ -66,6 +64,7 @@
     private static final int MONITOR_INTERVAL = 1; //in minutes
     private static final int LOW_MEMORY_NOTIFICATION_ID = 1;
     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
+    private static final int DEFAULT_THRESHOLD_MAX_BYTES = 500*1024*1024; // 500MB
     private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
     private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
     private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
@@ -271,13 +270,18 @@
      * any way
      */
     private long getMemThreshold() {
-        int value = Settings.Secure.getInt(
+        long value = Settings.Secure.getInt(
                               mContentResolver,
                               Settings.Secure.SYS_STORAGE_THRESHOLD_PERCENTAGE,
                               DEFAULT_THRESHOLD_PERCENTAGE);
         if(localLOGV) Slog.v(TAG, "Threshold Percentage="+value);
+        value *= mTotalMemory;
+        long maxValue = Settings.Secure.getInt(
+                mContentResolver,
+                Settings.Secure.SYS_STORAGE_THRESHOLD_MAX_BYTES,
+                DEFAULT_THRESHOLD_MAX_BYTES);
         //evaluate threshold value
-        return mTotalMemory*value;
+        return value < maxValue ? value : maxValue;
     }
 
     /*
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f1d5489..3c8fb01 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1324,6 +1324,7 @@
         ActivityThread at = ActivityThread.systemMain();
         mSystemThread = at;
         Context context = at.getSystemContext();
+        context.setTheme(android.R.style.Theme_Holo);
         m.mContext = context;
         m.mFactoryTest = factoryTest;
         m.mMainStack = new ActivityStack(m, context, true);
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index caaae1f..72ea7ce 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -26,6 +26,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.Process;
@@ -69,6 +70,7 @@
     int labelRes;           // the label information from the package mgr.
     int icon;               // resource identifier of activity's icon.
     int theme;              // resource identifier of activity's theme.
+    int realTheme;          // actual theme resource we will use, never 0.
     int windowFlags;        // custom window flags for preview window.
     TaskRecord task;        // the task this is in.
     long launchTime;        // when we starting launching this activity
@@ -246,6 +248,13 @@
             }
             icon = aInfo.getIconResource();
             theme = aInfo.getThemeResource();
+            realTheme = theme;
+            if (realTheme == 0) {
+                realTheme = aInfo.applicationInfo.targetSdkVersion
+                        < Build.VERSION_CODES.HONEYCOMB
+                        ? android.R.style.Theme
+                        : android.R.style.Theme_Holo;
+            }
             if ((aInfo.flags&ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                 windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
             }
@@ -266,8 +275,7 @@
             launchMode = aInfo.launchMode;
             
             AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
-                    theme != 0 ? theme : android.R.style.Theme,
-                    com.android.internal.R.styleable.Window);
+                    realTheme, com.android.internal.R.styleable.Window);
             fullscreen = ent != null && !ent.array.getBoolean(
                     com.android.internal.R.styleable.Window_windowIsFloating, false)
                     && !ent.array.getBoolean(