Merge "Enable stylus scale by default for apps targeting MNC+"
diff --git a/api/current.txt b/api/current.txt
index a126699..baadf5d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30888,6 +30888,7 @@
     method public int getEvdoSnr();
     method public int getGsmBitErrorRate();
     method public int getGsmSignalStrength();
+    method public int getLevel();
     method public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index e149048..d3be68b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33041,6 +33041,7 @@
     method public int getEvdoSnr();
     method public int getGsmBitErrorRate();
     method public int getGsmSignalStrength();
+    method public int getLevel();
     method public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
   }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10d6d01..ab5f811 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2541,11 +2541,16 @@
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
                 data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
                 AssistContent content = new AssistContent();
-                Intent intent = new Intent(r.activity.getIntent());
-                intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
-                intent.removeUnsafeExtras();
-                content.setIntent(intent);
+                Intent activityIntent = r.activity.getIntent();
+                if (activityIntent != null) {
+                    Intent intent = new Intent(activityIntent);
+                    intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+                    intent.removeUnsafeExtras();
+                    content.setIntent(intent);
+                } else {
+                    content.setIntent(new Intent());
+                }
                 r.activity.onProvideAssistContent(content);
                 data.putParcelable(AssistContent.ASSIST_KEY, content);
             }
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 179957d..9d1d312 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -597,6 +597,15 @@
         }
     }
 
+    /** @hide */
+    public long getNextWakeFromIdleTime() {
+        try {
+            return mService.getNextWakeFromIdleTime();
+        } catch (RemoteException ex) {
+            return Long.MAX_VALUE;
+        }
+    }
+
     /**
      * Gets information about the next alarm clock currently scheduled.
      *
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index d5719f5..327c00b 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -33,6 +33,7 @@
     boolean setTime(long millis);
     void setTimeZone(String zone);
     void remove(in PendingIntent operation);
+    long getNextWakeFromIdleTime();
     AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
 }
 
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 56a3954..1aef0c1 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -62,6 +62,211 @@
 <div class="toggle-content opened">
   <p id="rev21"><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 22.1.0</a> <em>(April 2015)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Changes for <a href="features.html#v4">v4 support library:</a></dt>
+      <dd>
+        <ul>
+
+          <li>Added the {@link android.support.v4.graphics.ColorUtils ColorUtils} class
+            to provide a set of color-related utility methods.
+          </li>
+          <li>Added the {@link android.support.v4.graphics.drawable.DrawableCompat#unwrap unwrap()} and
+            {@link android.support.v4.graphics.drawable.DrawableCompat#wrap wrap()} methods to the
+            {@link android.support.v4.graphics.drawable.DrawableCompat} class , allowing you to use
+            {@link android.support.v4.graphics.drawable.DrawableCompat#setTint setTint()},
+            {@link android.support.v4.graphics.drawable.DrawableCompat#setTintList setTintList()},
+            and {@link android.support.v4.graphics.drawable.DrawableCompat#setTintMode setTintMode()}
+            on all API level 4 or higher devices.
+          </li>
+          <li>Added the {@link android.support.v4.os.TraceCompat} class to
+            write trace events to the system trace buffer, which can then be collected and
+            visualized using the <a href="{@docRoot}tools/help/systrace.html">Systrace</a> tool.
+          </li>
+          <li>Added the {@link android.support.v4.util.CircularIntArray} class
+            to create circular integer array data structures.
+          </li>
+          <li>Added the {@link android.support.v4.util.CircularArray#clear clear()},
+            {@link android.support.v4.util.CircularArray#removeFromStart removeFromStart()},
+            and {@link android.support.v4.util.CircularArray#removeFromEnd removeFromEnd()}
+            methods to the {@link android.support.v4.util.CircularArray} class. Also, changed the
+            existing methods in this class to be non-final.
+          </li>
+          <li>Added the {@link android.support.v4.view.InputDeviceCompat}
+            as a helper class to access data in the
+            {@link android.support.v4.view.InputDeviceCompat} class.
+          </li>
+          <li>Added the {@link android.support.v4.view.LayoutInflaterCompat}
+            class as a helper class to access data in the
+            {@link android.support.v4.view.LayoutInflaterCompat} class
+            and added the {@link android.support.v4.view.LayoutInflaterFactory} interface.
+          </li>
+          <li>Added classes, methods, and interfaces to support nested scrolling.
+             <ul>
+               <li>Added the {@link android.support.v4.view.NestedScrollingChildHelper}
+               and {@link android.support.v4.view.NestedScrollingParentHelper}
+               helper classes for implementing nested scrolling parent and child views.</li>
+               <li>Added the {@link android.support.v4.view.NestedScrollingChild}
+               interface to be implemented by {@link android.view.View} subclasses.</li>
+               <li>Added the {@link android.support.v4.view.NestedScrollingParent}
+               and {@link android.support.v4.view.ScrollingView} interfaces to support
+               scrolling operations and provide scroll related APIs.</li>
+               <li>Added the
+                {@link android.support.v4.view.ViewGroupCompat#getNestedScrollAxes
+                getNestedScrollAxes()} method to the {@link android.support.v4.view.ViewGroupCompat}
+                class.</li>
+               <li>Added methods to the {@link android.support.v4.view.ViewParentCompat} class to
+                support nested scrolling.
+               </li>
+               <li>Added the {@link android.support.v4.widget.NestedScrollView}
+                 class to support nested scrolling parent and child on both new and old versions of
+                 Android.
+               </li>
+              </ul>
+          </li>
+          <li>Added methods and constants to the {@link android.support.v4.view.MotionEventCompat}
+            class for getting axis values and event source.
+         </li>
+         <li>Updated the {@link android.support.v4.view.accessibility.AccessibilityNodeInfoCompat}
+          class to add methods for errors, content invalidation and labels.
+         </li>
+         <li>Added the following interpolation classses for animation:
+           {@link android.support.v4.view.animation.FastOutLinearInInterpolator},
+           {@link android.support.v4.view.animation.FastOutSlowInInterpolator},
+           {@link android.support.v4.view.animation.LinearOutSlowInInterpolator},
+           {@link android.support.v4.view.animation.LinearOutSlowInInterpolator}, and
+           {@link android.support.v4.view.animation.PathInterpolatorCompat}.
+         </li>
+         <li>Added the {@link android.support.v4.widget.Space} class to create gaps between
+           components in general purpose layouts. This class is deprecated in the gridlayout library.
+         </li>
+         <li>Added the {@link android.support.v4.widget.TextViewCompat} class for accessing
+           features in a {@link android.widget.TextView}.
+         </li>
+         <li>Added a displacement parameter to the
+           {@link android.support.v4.widget.EdgeEffectCompat#onPull onPull()} method in the
+           {@link android.support.v4.widget.EdgeEffectCompat} class.
+         </li>
+
+       </ul>
+      </dd>
+
+
+  <dt>Changes for <a href="features.html#v7-appcompat">v7 appcompat library</a>:</dt>
+      <dd>
+        <ul>
+          <li>Added tint support to appcompat widgets, including
+            {@link android.support.v7.widget.AppCompatAutoCompleteTextView},
+            {@link android.support.v7.widget.AppCompatButton},
+            {@link android.support.v7.widget.AppCompatCheckBox},
+            {@link android.support.v7.widget.AppCompatCheckedTextView},
+            {@link android.support.v7.widget.AppCompatEditText},
+            {@link android.support.v7.widget.AppCompatMultiAutoCompleteTextView},
+            {@link android.support.v7.widget.AppCompatRadioButton},
+            {@link android.support.v7.widget.AppCompatRatingBar},
+            {@link android.support.v7.widget.AppCompatSpinner}, and
+            {@link android.support.v7.widget.AppCompatTextView}.
+          </li>
+          <li>Updated the {@link android.support.v7.app.AppCompatActivity} as the base
+            class for activities that use the support library action bar features. This class
+            replaces the deprecated {@link android.support.v7.app.ActionBarActivity}.
+          </li>
+          <li>Added the
+            {@link android.support.v7.app.AppCompatCallback} interface
+            to be implemented for AppCompat to be able to perform callbacks.
+          </li>
+            <li>Added the
+            {@link android.support.v7.app.AppCompatDelegate} abstract class
+            as a delegate you can use to extend AppCompat's support to any activity.
+          </li>
+          <li>Added the
+            {@link android.support.v7.app.AppCompatDialog} class
+            as the base class for AppCompat themed dialogs.
+          </li>
+          <li>Added the spinner style
+            {@link android.support.v7.app.AlertDialog} and
+            {@link android.support.v7.app.AlertDialog.Builder} classes to provide an AppCompat
+            themed {@link android.app.AlertDialog}.
+          </li>
+          <li>Added the {@link android.support.v7.graphics.Palette.Builder} class
+            for generating {@link android.support.v7.graphics.Palette} instances.
+            <ul>
+              <li>Added the
+                {@link android.support.v7.graphics.Palette#from}
+                method to the {@link android.support.v7.graphics.Palette} class to
+                start generating a Palette with the returned
+                {@link android.support.v7.graphics.Palette.Builder} instance.
+              </li>
+              <li>Deprecated the {@link android.support.v7.graphics.Palette#generate generate()} and
+                {@link android.support.v7.graphics.Palette#generateAsync generateAsync()} methods.
+              </li>
+             </ul>
+           </li>
+
+           <li>Added the
+             {@link android.support.v7.widget.GridLayout.Spec#getAbsoluteAlignment
+             getAbsoluteAlignment()} method to the {@link android.support.v7.widget.GridLayout.Spec}
+             class.
+           </li>
+           <li>Deprecated use of <code>app:theme</code> for styling
+             {@link android.support.v7.widget.Toolbar}. You can now use
+             <code>android:theme</code> for toolbars on all API level 7 and higher devices and
+             <code>android:theme</code> support for all widgets on API level 11 and higher devices.
+           </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for <a href="features.html#v17-leanback">v17 leanback library</a>:</dt>
+      <dd>
+        <ul>
+          <li> Added {@link android.support.v17.leanback.app.GuidedStepFragment},
+            {@link android.support.v17.leanback.widget.GuidanceStylist} and
+            {@link android.support.v17.leanback.widget.GuidedActionsStylist} to support
+            creating multi-step decision flows.
+          </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for <a href="features.html#v7-recyclerview">v7 recyclerview library</a>:</dt>
+      <dd>
+        <ul>
+           <li>Added {@link android.support.v7.util.SortedList} classes to display items in
+             a list order and provide notification of changes to the list.
+           </li>
+           <li>Added the {@link android.support.v7.widget.util.SortedListAdapterCallback} class
+             that can bind a sorted list to a
+             {@link android.support.v7.widget.RecyclerView.Adapter} class.
+           </li>
+        </ul>
+      </dd>
+
+
+      <dt>Changes for v8 renderscript library:</dt>
+      <dd>
+        <ul>
+          <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicHistogram} class for
+            use as a histogram filter.</li>
+          <li>Added the {@link android.support.v8.renderscript.ScriptIntrinsicResize} class for
+            performing a resize of a 2D allocation.
+          </li>
+        </ul>
+      </dd>
+
+    </dl>
+
+
+  </div>
+</div> <!-- end of collapsible section -->
+
+
+
+<div class="toggle-content closed">
+  <p id="rev21"><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
 />Android Support Library, revision 22</a> <em>(March 2015)</em>
   </p>
   <div class="toggle-content-toggleme">
@@ -159,6 +364,10 @@
         </ul>
       </dd>
 
+  </div>
+</div>
+
+
 
 
 <div class="toggle-content closed">
@@ -1113,4 +1322,3 @@
       <p>Initial release with the v4 library.</p>
   </div>
 </div>
-
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 8039b71..efe71d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -16,17 +16,47 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
+import libcore.io.IoUtils;
 import android.app.Activity;
 import android.app.Fragment;
-import android.content.pm.ResolveInfo;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
+import android.util.Log;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
 
+import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
@@ -34,20 +64,125 @@
 import com.google.common.collect.Maps;
 
 abstract class BaseActivity extends Activity {
+
+    static final String EXTRA_STATE = "state";
+
+    private final String mTag;
+    RootsCache mRoots;
+
     public abstract State getDisplayState();
-    public abstract RootInfo getCurrentRoot();
-    public abstract void onStateChanged();
-    public abstract void setRootsDrawerOpen(boolean open);
     public abstract void onDocumentPicked(DocumentInfo doc);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
-    public abstract DocumentInfo getCurrentDirectory();
-    public abstract void setPending(boolean pending);
-    public abstract void onStackPicked(DocumentStack stack);
-    public abstract void onPickRequested(DocumentInfo pickTarget);
-    public abstract void onAppPicked(ResolveInfo info);
-    public abstract void onRootPicked(RootInfo root, boolean closeDrawer);
-    public abstract void onSaveRequested(DocumentInfo replaceTarget);
-    public abstract void onSaveRequested(String mimeType, String displayName);
+    abstract void onTaskFinished(Uri... uris);
+    abstract void onDirectoryChanged(int anim);
+    abstract void updateActionBar();
+    abstract void saveStackBlocking();
+
+    public BaseActivity(String tag) {
+        mTag = tag;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mRoots = DocumentsApplication.getRootsCache(this);
+    }
+
+    void onStackRestored(boolean restored, boolean external) {}
+
+    void onRootPicked(RootInfo root) {
+        State state = getDisplayState();
+
+        // Clear entire backstack and start in new root
+        state.stack.root = root;
+        state.stack.clear();
+        state.stackTouched = true;
+
+        if (!mRoots.isRecentsRoot(root)) {
+            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+        } else {
+            onCurrentDirectoryChanged(ANIM_SIDE);
+        }
+    }
+
+    void expandMenus(Menu menu) {
+        for (int i = 0; i < menu.size(); i++) {
+            final MenuItem item = menu.getItem(i);
+            switch (item.getItemId()) {
+                case R.id.menu_advanced:
+                case R.id.menu_file_size:
+                    break;
+                default:
+                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int id = item.getItemId();
+        if (id == android.R.id.home) {
+            onBackPressed();
+            return true;
+        } else if (id == R.id.menu_create_dir) {
+            CreateDirectoryFragment.show(getFragmentManager());
+            return true;
+        } else if (id == R.id.menu_search) {
+            return false;
+        } else if (id == R.id.menu_sort_name) {
+            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
+            return true;
+        } else if (id == R.id.menu_sort_date) {
+            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
+            return true;
+        } else if (id == R.id.menu_sort_size) {
+            setUserSortOrder(State.SORT_ORDER_SIZE);
+            return true;
+        } else if (id == R.id.menu_grid) {
+            setUserMode(State.MODE_GRID);
+            return true;
+        } else if (id == R.id.menu_list) {
+            setUserMode(State.MODE_LIST);
+            return true;
+        } else if (id == R.id.menu_advanced) {
+            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
+            return true;
+        } else if (id == R.id.menu_file_size) {
+            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
+            return true;
+        } else if (id == R.id.menu_settings) {
+            final RootInfo root = getCurrentRoot();
+            final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
+            intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
+                    DocumentsContract.Root.MIME_TYPE_ITEM);
+            startActivity(intent);
+            return true;
+        }
+
+        return super.onOptionsItemSelected(item);
+    }
+
+    /**
+     * Call this when directory changes. Prior to root fragment update
+     * the (abstract) directoryChanged method will be called.
+     * @param anim
+     */
+    final void onCurrentDirectoryChanged(int anim) {
+        onDirectoryChanged(anim);
+
+        final RootsFragment roots = RootsFragment.get(getFragmentManager());
+        if (roots != null) {
+            roots.onCurrentRootChanged();
+        }
+
+        updateActionBar();
+        invalidateOptionsMenu();
+    }
+
+    final String getCallingPackageMaybeExtra() {
+        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+        return (extra != null) ? extra : getCallingPackage();
+    }
 
     public static BaseActivity get(Fragment fragment) {
         return (BaseActivity) fragment.getActivity();
@@ -169,4 +304,382 @@
             }
         };
     }
+
+    void setDisplayAdvancedDevices(boolean display) {
+        State state = getDisplayState();
+        LocalPreferences.setDisplayAdvancedDevices(this, display);
+        state.showAdvanced = state.forceAdvanced | display;
+        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    void setDisplayFileSize(boolean display) {
+        LocalPreferences.setDisplayFileSize(this, display);
+        getDisplayState().showSize = display;
+        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    void onStateChanged() {
+        invalidateOptionsMenu();
+    }
+
+    /**
+     * Set state sort order based on explicit user action.
+     */
+    void setUserSortOrder(int sortOrder) {
+        getDisplayState().userSortOrder = sortOrder;
+        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+    }
+
+    /**
+     * Set state mode based on explicit user action.
+     */
+    void setUserMode(int mode) {
+        getDisplayState().userMode = mode;
+        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+    }
+
+    void setPending(boolean pending) {
+        final SaveFragment save = SaveFragment.get(getFragmentManager());
+        if (save != null) {
+            save.setPending(pending);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle state) {
+        super.onSaveInstanceState(state);
+        state.putParcelable(EXTRA_STATE, getDisplayState());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+    }
+
+    RootInfo getCurrentRoot() {
+        State state = getDisplayState();
+        if (state.stack.root != null) {
+            return state.stack.root;
+        } else {
+            return mRoots.getRecentsRoot();
+        }
+    }
+
+    public DocumentInfo getCurrentDirectory() {
+        return getDisplayState().stack.peek();
+    }
+
+    public Executor getCurrentExecutor() {
+        final DocumentInfo cwd = getCurrentDirectory();
+        if (cwd != null && cwd.authority != null) {
+            return ProviderExecutor.forAuthority(cwd.authority);
+        } else {
+            return AsyncTask.THREAD_POOL_EXECUTOR;
+        }
+    }
+
+    public void onStackPicked(DocumentStack stack) {
+        try {
+            // Update the restored stack to ensure we have freshest data
+            stack.updateDocuments(getContentResolver());
+
+            State state = getDisplayState();
+            state.stack = stack;
+            state.stackTouched = true;
+            onCurrentDirectoryChanged(ANIM_SIDE);
+
+        } catch (FileNotFoundException e) {
+            Log.w(mTag, "Failed to restore stack: " + e);
+        }
+    }
+
+    final class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private RootInfo mRoot;
+
+        public PickRootTask(RootInfo root) {
+            mRoot = root;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            try {
+                final Uri uri = DocumentsContract.buildDocumentUri(
+                        mRoot.authority, mRoot.documentId);
+                return DocumentInfo.fromUri(getContentResolver(), uri);
+            } catch (FileNotFoundException e) {
+                Log.w(mTag, "Failed to find root", e);
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            if (result != null) {
+                State state = getDisplayState();
+                state.stack.push(result);
+                state.stackTouched = true;
+                onCurrentDirectoryChanged(ANIM_SIDE);
+            }
+        }
+    }
+
+    final class RestoreStackTask extends AsyncTask<Void, Void, Void> {
+        private volatile boolean mRestoredStack;
+        private volatile boolean mExternal;
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            State state = getDisplayState();
+            RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
+
+            // Restore last stack for calling package
+            final String packageName = getCallingPackageMaybeExtra();
+            final Cursor cursor = getContentResolver()
+                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
+            try {
+                if (cursor.moveToFirst()) {
+                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(ResumeColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, state.stack);
+                    mRestoredStack = true;
+                }
+            } catch (IOException e) {
+                Log.w(mTag, "Failed to resume: " + e);
+            } finally {
+                IoUtils.closeQuietly(cursor);
+            }
+
+            if (mRestoredStack) {
+                // Update the restored stack to ensure we have freshest data
+                final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+                try {
+                    state.stack.updateRoot(matchingRoots);
+                    state.stack.updateDocuments(getContentResolver());
+                } catch (FileNotFoundException e) {
+                    Log.w(mTag, "Failed to restore stack: " + e);
+                    state.stack.reset();
+                    mRestoredStack = false;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            if (isDestroyed()) return;
+            getDisplayState().restored = true;
+            onCurrentDirectoryChanged(ANIM_NONE);
+
+            onStackRestored(mRestoredStack, mExternal);
+
+            getDisplayState().restored = true;
+            onCurrentDirectoryChanged(ANIM_NONE);
+        }
+    }
+
+    final class ItemSelectedListener implements OnItemSelectedListener {
+
+        boolean mIgnoreNextNavigation;
+
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mIgnoreNextNavigation) {
+                mIgnoreNextNavigation = false;
+                return;
+            }
+
+            State state = getDisplayState();
+            while (state.stack.size() > position + 1) {
+                state.stackTouched = true;
+                state.stack.pop();
+            }
+            onCurrentDirectoryChanged(ANIM_UP);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Ignored
+        }
+    }
+
+    /**
+     * Class providing toolbar with runtime access to useful activity data.
+     */
+    final class StackAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return getDisplayState().stack.size();
+        }
+
+        @Override
+        public DocumentInfo getItem(int position) {
+            State state = getDisplayState();
+            return state.stack.get(state.stack.size() - position - 1);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir_title, parent, false);
+            }
+
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+            } else {
+                title.setText(doc.displayName);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir, parent, false);
+            }
+
+            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+                subdir.setVisibility(View.GONE);
+            } else {
+                title.setText(doc.displayName);
+                subdir.setVisibility(View.VISIBLE);
+            }
+
+            return convertView;
+        }
+    }
+
+    /**
+     * Facade over the various search parts in the menu.
+     */
+    final class SearchManager implements
+            SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener {
+
+        protected boolean mSearchExpanded;
+        protected boolean mIgnoreNextClose;
+        protected boolean mIgnoreNextCollapse;
+
+        private MenuItem mMenu;
+        private SearchView mView;
+
+        public void install(MenuItem menu) {
+            assert(mMenu == null);
+            mMenu = menu;
+            mView = (SearchView) menu.getActionView();
+
+            mMenu.setOnActionExpandListener(this);
+            mView.setOnQueryTextListener(this);
+            mView.setOnCloseListener(this);
+        }
+
+        /**
+         * @param root Info about the current directory.
+         */
+        void update(RootInfo root) {
+            if (mMenu == null) {
+                Log.d(mTag, "showMenu called before Search MenuItem installed.");
+                return;
+            }
+            State state = getDisplayState();
+            if (state.currentSearch != null) {
+                mMenu.expandActionView();
+
+                mView.setIconified(false);
+                mView.clearFocus();
+                mView.setQuery(state.currentSearch, false);
+            } else {
+                mIgnoreNextClose = true;
+                mView.setIconified(true);
+                mView.clearFocus();
+
+                mIgnoreNextCollapse = true;
+                mMenu.collapseActionView();
+            }
+
+            showMenu(root != null
+                    && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0));
+        }
+
+        void showMenu(boolean visible) {
+            if (mMenu == null) {
+                Log.d(mTag, "showMenu called before Search MenuItem installed.");
+                return;
+            }
+            mMenu.setVisible(visible);
+        }
+
+        boolean isSearching() {
+            return getDisplayState().currentSearch != null;
+        }
+
+        boolean isExpanded() {
+            return mSearchExpanded;
+        }
+
+        @Override
+        public boolean onClose() {
+            mSearchExpanded = false;
+            if (mIgnoreNextClose) {
+                mIgnoreNextClose = false;
+                return false;
+            }
+
+            getDisplayState().currentSearch = null;
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return false;
+        }
+        @Override
+        public boolean onMenuItemActionExpand(MenuItem item) {
+            mSearchExpanded = true;
+            updateActionBar();
+            return true;
+        }
+
+        @Override
+        public boolean onMenuItemActionCollapse(MenuItem item) {
+            mSearchExpanded = false;
+            if (mIgnoreNextCollapse) {
+                mIgnoreNextCollapse = false;
+                return true;
+            }
+
+            getDisplayState().currentSearch = null;
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return true;
+        }
+        @Override
+        public boolean onQueryTextSubmit(String query) {
+            mSearchExpanded = true;
+            getDisplayState().currentSearch = query;
+            mView.clearFocus();
+            onCurrentDirectoryChanged(ANIM_NONE);
+            return true;
+        }
+
+        @Override
+        public boolean onQueryTextChange(String newText) {
+            return false;
+        }
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 37a14c6..7cf58cc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -28,7 +28,6 @@
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.Fragment;
@@ -311,8 +310,9 @@
                 updateDisplayState();
 
                 // When launched into empty recents, show drawer
-                if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
-                    ((BaseActivity) context).setRootsDrawerOpen(true);
+                if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched &&
+                        context instanceof DocumentsActivity) {
+                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
 
                 // Restore any previous instance state
@@ -503,7 +503,8 @@
             open.setVisible(!manageOrBrowse);
             share.setVisible(manageOrBrowse);
             delete.setVisible(manageOrBrowse);
-            copy.setVisible(manageOrBrowse);
+            // Disable copying from the Recents view.
+            copy.setVisible(manageOrBrowse && mType != TYPE_RECENT_OPEN);
 
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index a2a789f..9d828de 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -21,16 +21,17 @@
 import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
 import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
-import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_COPY_DESTINATION;
-import static com.android.documentsui.BaseActivity.State.MODE_GRID;
-import static com.android.documentsui.BaseActivity.State.MODE_LIST;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
 import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
 
+import java.util.Arrays;
+import java.util.List;
+
 import android.app.Activity;
+import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
@@ -42,7 +43,6 @@
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -54,51 +54,27 @@
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Spinner;
-import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
-import libcore.io.IoUtils;
-
 import com.android.documentsui.RecentsProvider.RecentColumns;
 import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Executor;
-
 public class DocumentsActivity extends BaseActivity {
+    private static final int CODE_FORWARD = 42;
     public static final String TAG = "Documents";
 
-    private static final String EXTRA_STATE = "state";
-
-    private static final int CODE_FORWARD = 42;
-
     private boolean mShowAsDialog;
 
-    private SearchView mSearchView;
-
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
 
@@ -110,21 +86,20 @@
 
     private DirectoryContainerView mDirectoryContainer;
 
-    private boolean mIgnoreNextNavigation;
-    private boolean mIgnoreNextClose;
-    private boolean mIgnoreNextCollapse;
-
-    private boolean mSearchExpanded;
-
-    private RootsCache mRoots;
     private State mState;
 
+    private SearchManager mSearchManager;
+    private ItemSelectedListener mStackListener;
+    private BaseAdapter mStackAdapter;
+
+    public DocumentsActivity() {
+        super(TAG);
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mRoots = DocumentsApplication.getRootsCache(this);
-
         setResult(Activity.RESULT_CANCELED);
         setContentView(R.layout.activity);
 
@@ -157,16 +132,16 @@
 
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
 
-        if (icicle != null) {
-            mState = icicle.getParcelable(EXTRA_STATE);
-        } else {
-            buildDefaultState();
-        }
+        mState = (icicle != null)
+                ? icicle.<State>getParcelable(EXTRA_STATE)
+                : buildDefaultState();
 
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
         mToolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
 
+        mStackAdapter = new StackAdapter();
+        mStackListener = new ItemSelectedListener();
         mToolbarStack = (Spinner) findViewById(R.id.stack);
         mToolbarStack.setOnItemSelectedListener(mStackListener);
 
@@ -176,6 +151,7 @@
                     android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
         }
 
+        mSearchManager = new SearchManager();
         setActionBar(mToolbar);
 
         // Hide roots when we're managing a specific root
@@ -220,55 +196,57 @@
         }
     }
 
-    private void buildDefaultState() {
-        mState = new State();
+    private State buildDefaultState() {
+        State state = new State();
 
         final Intent intent = getIntent();
         final String action = intent.getAction();
         if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
-            mState.action = ACTION_OPEN;
+            state.action = ACTION_OPEN;
         } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
-            mState.action = ACTION_CREATE;
+            state.action = ACTION_CREATE;
         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
-            mState.action = ACTION_GET_CONTENT;
+            state.action = ACTION_GET_CONTENT;
         } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
-            mState.action = ACTION_OPEN_TREE;
+            state.action = ACTION_OPEN_TREE;
         } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
-            mState.action = ACTION_MANAGE;
+            state.action = ACTION_MANAGE;
         } else if (DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT.equals(action)) {
-            mState.action = ACTION_BROWSE;
+            state.action = ACTION_BROWSE;
         } else if (DocumentsIntent.ACTION_OPEN_COPY_DESTINATION.equals(action)) {
-            mState.action = ACTION_OPEN_COPY_DESTINATION;
+            state.action = ACTION_OPEN_COPY_DESTINATION;
         }
 
-        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
-            mState.allowMultiple = intent.getBooleanExtra(
+        if (state.action == ACTION_OPEN || state.action == ACTION_GET_CONTENT) {
+            state.allowMultiple = intent.getBooleanExtra(
                     Intent.EXTRA_ALLOW_MULTIPLE, false);
         }
 
-        if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
-            mState.acceptMimes = new String[] { "*/*" };
-            mState.allowMultiple = true;
+        if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+            state.acceptMimes = new String[] { "*/*" };
+            state.allowMultiple = true;
         } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
-            mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
+            state.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
         } else {
-            mState.acceptMimes = new String[] { intent.getType() };
+            state.acceptMimes = new String[] { intent.getType() };
         }
 
-        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
-        mState.showAdvanced = mState.forceAdvanced
+        state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = state.forceAdvanced
                 | LocalPreferences.getDisplayAdvancedDevices(this);
 
-        if (mState.action == ACTION_MANAGE || mState.action == ACTION_BROWSE) {
-            mState.showSize = true;
+        if (state.action == ACTION_MANAGE || state.action == ACTION_BROWSE) {
+            state.showSize = true;
         } else {
-            mState.showSize = LocalPreferences.getDisplayFileSize(this);
+            state.showSize = LocalPreferences.getDisplayFileSize(this);
         }
-        if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
-            mState.directoryCopy = intent.getBooleanExtra(
+        if (state.action == ACTION_OPEN_COPY_DESTINATION) {
+            state.directoryCopy = intent.getBooleanExtra(
                     BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
         }
+
+        return state;
     }
 
     private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
@@ -290,7 +268,7 @@
             mState.restored = true;
 
             if (root != null) {
-                onRootPicked(root, true);
+                onRootPicked(root);
             } else {
                 Log.w(TAG, "Failed to find root: " + mRootUri);
                 finish();
@@ -298,71 +276,55 @@
         }
     }
 
-    private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
+    @Override
+    void onStackRestored(boolean restored, boolean external) {
+        // Show drawer when no stack restored, but only when requesting
+        // non-visual content. However, if we last used an external app,
+        // drawer is always shown.
 
-        @Override
-        protected Void doInBackground(Void... params) {
-            // Restore last stack for calling package
-            final String packageName = getCallingPackageMaybeExtra();
-            final Cursor cursor = getContentResolver()
-                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
-            try {
-                if (cursor.moveToFirst()) {
-                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, mState.stack);
-                    mRestoredStack = true;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to resume: " + e);
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            if (mRestoredStack) {
-                // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Failed to restore stack: " + e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
+        boolean showDrawer = false;
+        if (!restored) {
+            showDrawer = true;
+        }
+        if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+            showDrawer = false;
+        }
+        if (external && mState.action == ACTION_GET_CONTENT) {
+            showDrawer = true;
         }
 
-        @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) return;
-            mState.restored = true;
+        if (showDrawer) {
+            setRootsDrawerOpen(true);
+        }
+    }
 
-            // Show drawer when no stack restored, but only when requesting
-            // non-visual content. However, if we last used an external app,
-            // drawer is always shown.
+    public void onAppPicked(ResolveInfo info) {
+        final Intent intent = new Intent(getIntent());
+        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        intent.setComponent(new ComponentName(
+                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
+        startActivityForResult(intent, CODE_FORWARD);
+    }
 
-            boolean showDrawer = false;
-            if (!mRestoredStack) {
-                showDrawer = true;
-            }
-            if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
-                showDrawer = false;
-            }
-            if (mExternal && mState.action == ACTION_GET_CONTENT) {
-                showDrawer = true;
-            }
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult() code=" + resultCode);
 
-            if (showDrawer) {
-                setRootsDrawerOpen(true);
-            }
+        // Only relay back results when not canceled; otherwise stick around to
+        // let the user pick another app/backend.
+        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
 
-            onCurrentDirectoryChanged(ANIM_NONE);
+            // Remember that we last picked via external app
+            final String packageName = getCallingPackageMaybeExtra();
+            final ContentValues values = new ContentValues();
+            values.put(ResumeColumns.EXTERNAL, 1);
+            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
+
+            // Pass back result to original caller
+            setResult(resultCode, data);
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
         }
     }
 
@@ -397,7 +359,6 @@
         updateActionBar();
     }
 
-    @Override
     public void setRootsDrawerOpen(boolean open) {
         if (!mShowAsDialog) {
             if (open) {
@@ -416,6 +377,7 @@
         }
     }
 
+    @Override
     public void updateActionBar() {
         if (mRootsToolbar != null) {
             if (mState.action == ACTION_OPEN ||
@@ -447,7 +409,7 @@
             });
         }
 
-        if (mSearchExpanded) {
+        if (mSearchManager.isExpanded()) {
             mToolbar.setTitle(null);
             mToolbarStack.setVisibility(View.GONE);
             mToolbarStack.setAdapter(null);
@@ -461,7 +423,7 @@
                 mToolbarStack.setVisibility(View.VISIBLE);
                 mToolbarStack.setAdapter(mStackAdapter);
 
-                mIgnoreNextNavigation = true;
+                mStackListener.mIgnoreNextNavigation = true;
                 mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
             }
         }
@@ -469,79 +431,18 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
+        boolean showMenu = super.onCreateOptionsMenu(menu);
+
         getMenuInflater().inflate(R.menu.activity, menu);
 
         // Most actions are visible when showing as dialog
         if (mShowAsDialog) {
-            for (int i = 0; i < menu.size(); i++) {
-                final MenuItem item = menu.getItem(i);
-                switch (item.getItemId()) {
-                    case R.id.menu_advanced:
-                    case R.id.menu_file_size:
-                        break;
-                    default:
-                        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-                }
-            }
+            expandMenus(menu);
         }
 
-        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
-        mSearchView = (SearchView) searchMenu.getActionView();
-        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
-            @Override
-            public boolean onQueryTextSubmit(String query) {
-                mSearchExpanded = true;
-                mState.currentSearch = query;
-                mSearchView.clearFocus();
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
+        this.mSearchManager.install(menu.findItem(R.id.menu_search));
 
-            @Override
-            public boolean onQueryTextChange(String newText) {
-                return false;
-            }
-        });
-
-        searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
-            @Override
-            public boolean onMenuItemActionExpand(MenuItem item) {
-                mSearchExpanded = true;
-                updateActionBar();
-                return true;
-            }
-
-            @Override
-            public boolean onMenuItemActionCollapse(MenuItem item) {
-                mSearchExpanded = false;
-                if (mIgnoreNextCollapse) {
-                    mIgnoreNextCollapse = false;
-                    return true;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
-        });
-
-        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
-            @Override
-            public boolean onClose() {
-                mSearchExpanded = false;
-                if (mIgnoreNextClose) {
-                    mIgnoreNextClose = false;
-                    return false;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return false;
-            }
-        });
-
-        return true;
+        return showMenu;
     }
 
     @Override
@@ -554,7 +455,6 @@
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem search = menu.findItem(R.id.menu_search);
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
@@ -564,36 +464,23 @@
         final MenuItem settings = menu.findItem(R.id.menu_settings);
 
         sort.setVisible(cwd != null);
-        grid.setVisible(mState.derivedMode != MODE_GRID);
-        list.setVisible(mState.derivedMode != MODE_LIST);
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
 
-        if (mState.currentSearch != null) {
-            // Search uses backend ranking; no sorting
-            sort.setVisible(false);
 
-            search.expandActionView();
+        mSearchManager.update(root);
 
-            mSearchView.setIconified(false);
-            mSearchView.clearFocus();
-            mSearchView.setQuery(mState.currentSearch, false);
-        } else {
-            mIgnoreNextClose = true;
-            mSearchView.setIconified(true);
-            mSearchView.clearFocus();
-
-            mIgnoreNextCollapse = true;
-            search.collapseActionView();
-        }
+        // Search uses backend ranking; no sorting
+        sort.setVisible(mSearchManager.isSearching());
 
         // Only sort by size when visible
         sortSize.setVisible(mState.showSize);
 
-        boolean searchVisible;
         boolean fileSizeVisible = !(mState.action == ACTION_MANAGE
                 || mState.action == ACTION_BROWSE);
         if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
             createDir.setVisible(cwd != null && cwd.isCreateSupported());
-            searchVisible = false;
+            mSearchManager.showMenu(false);
 
             // No display options in recent directories
             if (cwd == null) {
@@ -607,14 +494,8 @@
             }
         } else {
             createDir.setVisible(false);
-
-            searchVisible = root != null
-                    && ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
         }
 
-        // TODO: close any search in-progress when hiding
-        search.setVisible(searchVisible);
-
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
@@ -634,90 +515,7 @@
         if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
             return true;
         }
-
-        final int id = item.getItemId();
-        if (id == android.R.id.home) {
-            onBackPressed();
-            return true;
-        } else if (id == R.id.menu_create_dir) {
-            CreateDirectoryFragment.show(getFragmentManager());
-            return true;
-        } else if (id == R.id.menu_search) {
-            return false;
-        } else if (id == R.id.menu_sort_name) {
-            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
-            return true;
-        } else if (id == R.id.menu_sort_date) {
-            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
-            return true;
-        } else if (id == R.id.menu_sort_size) {
-            setUserSortOrder(State.SORT_ORDER_SIZE);
-            return true;
-        } else if (id == R.id.menu_grid) {
-            setUserMode(State.MODE_GRID);
-            return true;
-        } else if (id == R.id.menu_list) {
-            setUserMode(State.MODE_LIST);
-            return true;
-        } else if (id == R.id.menu_advanced) {
-            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
-            return true;
-        } else if (id == R.id.menu_file_size) {
-            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
-            return true;
-        } else if (id == R.id.menu_settings) {
-            final RootInfo root = getCurrentRoot();
-            final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
-            intent.setDataAndType(DocumentsContract.buildRootUri(root.authority, root.rootId),
-                    DocumentsContract.Root.MIME_TYPE_ITEM);
-            startActivity(intent);
-            return true;
-        } else {
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private void setDisplayAdvancedDevices(boolean display) {
-        LocalPreferences.setDisplayAdvancedDevices(this, display);
-        mState.showAdvanced = mState.forceAdvanced | display;
-        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    private void setDisplayFileSize(boolean display) {
-        LocalPreferences.setDisplayFileSize(this, display);
-        mState.showSize = display;
-        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    public void onStateChanged() {
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Set state sort order based on explicit user action.
-     */
-    private void setUserSortOrder(int sortOrder) {
-        mState.userSortOrder = sortOrder;
-        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
-    }
-
-    /**
-     * Set state mode based on explicit user action.
-     */
-    private void setUserMode(int mode) {
-        mState.userMode = mode;
-        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
-    }
-
-    @Override
-    public void setPending(boolean pending) {
-        final SaveFragment save = SaveFragment.get(getFragmentManager());
-        if (save != null) {
-            save.setPending(pending);
-        }
+        return super.onOptionsItemSelected(item);
     }
 
     @Override
@@ -740,131 +538,12 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle state) {
-        super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, mState);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle state) {
-        super.onRestoreInstanceState(state);
-    }
-
-    private BaseAdapter mStackAdapter = new BaseAdapter() {
-        @Override
-        public int getCount() {
-            return mState.stack.size();
-        }
-
-        @Override
-        public DocumentInfo getItem(int position) {
-            return mState.stack.get(mState.stack.size() - position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir_title, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-            } else {
-                title.setText(doc.displayName);
-            }
-
-            return convertView;
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir, parent, false);
-            }
-
-            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-                subdir.setVisibility(View.GONE);
-            } else {
-                title.setText(doc.displayName);
-                subdir.setVisibility(View.VISIBLE);
-            }
-
-            return convertView;
-        }
-    };
-
-    private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mIgnoreNextNavigation) {
-                mIgnoreNextNavigation = false;
-                return;
-            }
-
-            while (mState.stack.size() > position + 1) {
-                mState.stackTouched = true;
-                mState.stack.pop();
-            }
-            onCurrentDirectoryChanged(ANIM_UP);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // Ignored
-        }
-    };
-
-    @Override
-    public RootInfo getCurrentRoot() {
-        if (mState.stack.root != null) {
-            return mState.stack.root;
-        } else {
-            return mRoots.getRecentsRoot();
-        }
-    }
-
-    @Override
-    public DocumentInfo getCurrentDirectory() {
-        return mState.stack.peek();
-    }
-
-    private String getCallingPackageMaybeExtra() {
-        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
-        return (extra != null) ? extra : getCallingPackage();
-    }
-
-    public Executor getCurrentExecutor() {
-        final DocumentInfo cwd = getCurrentDirectory();
-        if (cwd != null && cwd.authority != null) {
-            return ProviderExecutor.forAuthority(cwd.authority);
-        } else {
-            return AsyncTask.THREAD_POOL_EXECUTOR;
-        }
-    }
-
-    @Override
     public State getDisplayState() {
         return mState;
     }
 
-    private void onCurrentDirectoryChanged(int anim) {
+    @Override
+    void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -883,7 +562,7 @@
                 // Start recents in grid when requesting visual things
                 final boolean visualMimes = MimePredicate.mimeMatches(
                         MimePredicate.VISUAL_MIMES, mState.acceptMimes);
-                mState.userMode = visualMimes ? MODE_GRID : MODE_LIST;
+                mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
                 mState.derivedMode = mState.userMode;
             }
         } else {
@@ -913,108 +592,20 @@
                 pick.setPickTarget(mState.action, cwd, displayName);
             }
         }
+    }
 
-        final RootsFragment roots = RootsFragment.get(fm);
-        if (roots != null) {
-            roots.onCurrentRootChanged();
-        }
+    void onSaveRequested(DocumentInfo replaceTarget) {
+        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+    }
 
-        updateActionBar();
-        invalidateOptionsMenu();
-        dumpStack();
+    void onSaveRequested(String mimeType, String displayName) {
+        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
     }
 
     @Override
-    public void onStackPicked(DocumentStack stack) {
-        try {
-            // Update the restored stack to ensure we have freshest data
-            stack.updateDocuments(getContentResolver());
-
-            mState.stack = stack;
-            mState.stackTouched = true;
-            onCurrentDirectoryChanged(ANIM_SIDE);
-
-        } catch (FileNotFoundException e) {
-            Log.w(TAG, "Failed to restore stack: " + e);
-        }
-    }
-
-    @Override
-    public void onRootPicked(RootInfo root, boolean closeDrawer) {
-        // Clear entire backstack and start in new root
-        mState.stack.root = root;
-        mState.stack.clear();
-        mState.stackTouched = true;
-
-        if (!mRoots.isRecentsRoot(root)) {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
-        } else {
-            onCurrentDirectoryChanged(ANIM_SIDE);
-        }
-
-        if (closeDrawer) {
-            setRootsDrawerOpen(false);
-        }
-    }
-
-    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
-        private RootInfo mRoot;
-
-        public PickRootTask(RootInfo root) {
-            mRoot = root;
-        }
-
-        @Override
-        protected DocumentInfo doInBackground(Void... params) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(
-                        mRoot.authority, mRoot.documentId);
-                return DocumentInfo.fromUri(getContentResolver(), uri);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Failed to find root", e);
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPostExecute(DocumentInfo result) {
-            if (result != null) {
-                mState.stack.push(result);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            }
-        }
-    }
-
-    @Override
-    public void onAppPicked(ResolveInfo info) {
-        final Intent intent = new Intent(getIntent());
-        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-        intent.setComponent(new ComponentName(
-                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
-        startActivityForResult(intent, CODE_FORWARD);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "onActivityResult() code=" + resultCode);
-
-        // Only relay back results when not canceled; otherwise stick around to
-        // let the user pick another app/backend.
-        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
-            // Remember that we last picked via external app
-            final String packageName = getCallingPackageMaybeExtra();
-            final ContentValues values = new ContentValues();
-            values.put(ResumeColumns.EXTERNAL, 1);
-            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
-            // Pass back result to original caller
-            setResult(resultCode, data);
-            finish();
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
+    void onRootPicked(RootInfo root) {
+        super.onRootPicked(root);
+        setRootsDrawerOpen(false);
     }
 
     @Override
@@ -1076,17 +667,6 @@
         }
     }
 
-    @Override
-    public void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
     public void onPickRequested(DocumentInfo pickTarget) {
         Uri result;
         if (mState.action == ACTION_OPEN_TREE) {
@@ -1101,7 +681,8 @@
         new PickFinishTask(result).executeOnExecutor(getCurrentExecutor());
     }
 
-    private void saveStackBlocking() {
+    @Override
+    void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
@@ -1124,7 +705,8 @@
         resolver.insert(RecentsProvider.buildResume(packageName), values);
     }
 
-    private void onFinished(Uri... uris) {
+    @Override
+    void onTaskFinished(Uri... uris) {
         Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
@@ -1159,7 +741,52 @@
         finish();
     }
 
-    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+    public static DocumentsActivity get(Fragment fragment) {
+        return (DocumentsActivity) fragment.getActivity();
+    }
+
+    private final class PickFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri mUri;
+
+        public PickFinishTask(Uri uri) {
+            mUri = uri;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onTaskFinished(mUri);
+        }
+    }
+
+    final class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri[] mUris;
+
+        public ExistingFinishTask(Uri... uris) {
+            mUris = uris;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onTaskFinished(mUris);
+        }
+    }
+
+    /**
+     * Task that creates a new document in the background.
+     */
+    final class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
         private final String mMimeType;
         private final String mDisplayName;
 
@@ -1201,7 +828,7 @@
         @Override
         protected void onPostExecute(Uri result) {
             if (result != null) {
-                onFinished(result);
+                onTaskFinished(result);
             } else {
                 Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
                         .show();
@@ -1210,50 +837,4 @@
             setPending(false);
         }
     }
-
-    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri[] mUris;
-
-        public ExistingFinishTask(Uri... uris) {
-            mUris = uris;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUris);
-        }
-    }
-
-    private class PickFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri mUri;
-
-        public PickFinishTask(Uri uri) {
-            mUri = uri;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUri);
-        }
-    }
-
-    private void dumpStack() {
-        Log.d(TAG, "Current stack: ");
-        Log.d(TAG, " * " + mState.stack.root);
-        for (DocumentInfo doc : mState.stack) {
-            Log.d(TAG, " +-- " + doc);
-        }
-    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 7ea51b9..e899379 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -75,7 +75,7 @@
     private View.OnClickListener mPickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final BaseActivity activity = BaseActivity.get(PickFragment.this);
+            final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this);
             activity.onPickRequested(mPickTarget);
         }
     };
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 26aecc5..e11d7d9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -109,8 +109,9 @@
                 mAdapter.swapStacks(data);
 
                 // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.stackTouched) {
-                    ((BaseActivity) context).setRootsDrawerOpen(true);
+                if (mAdapter.isEmpty() && !state.stackTouched &&
+                        context instanceof DocumentsActivity) {
+                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
                 }
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index ed5e123..fd67a77 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -174,11 +174,12 @@
     private OnItemClickListener mItemListener = new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final BaseActivity activity = BaseActivity.get(RootsFragment.this);
-            final Item item = mAdapter.getItem(position);
+            Item item = mAdapter.getItem(position);
             if (item instanceof RootItem) {
-                activity.onRootPicked(((RootItem) item).root, true);
+                BaseActivity activity = BaseActivity.get(RootsFragment.this);
+                activity.onRootPicked(((RootItem) item).root);
             } else if (item instanceof AppItem) {
+                DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
                 activity.onAppPicked(((AppItem) item).info);
             } else {
                 throw new IllegalStateException("Unknown root: " + item);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index a13fccc..ce98db2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -113,7 +113,7 @@
     private View.OnClickListener mSaveListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final BaseActivity activity = BaseActivity.get(SaveFragment.this);
+            final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
             if (mReplaceTarget != null) {
                 activity.onSaveRequested(mReplaceTarget);
             } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
index 976f21d..f89b182 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -16,50 +16,29 @@
 
 package com.android.documentsui;
 
-
 import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
 import android.app.Activity;
-import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
-import android.content.ComponentName;
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Debug;
 import android.provider.DocumentsContract;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.provider.DocumentsContract.Root;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Spinner;
-import android.widget.TextView;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
@@ -70,55 +49,37 @@
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.Executor;
 
+/**
+ * Activity providing a directly launchable file management activity.
+ */
 public class StandaloneActivity extends BaseActivity {
     public static final String TAG = "StandaloneFileManagement";
 
-    private static final String EXTRA_STATE = "state";
-
-    private static final int CODE_FORWARD = 42;
-
-    private SearchView mSearchView;
-
     private Toolbar mToolbar;
     private Spinner mToolbarStack;
-
     private Toolbar mRootsToolbar;
-
-    private ActionBarDrawerToggle mDrawerToggle;
-
     private DirectoryContainerView mDirectoryContainer;
-
-    private boolean mIgnoreNextNavigation;
-    private boolean mIgnoreNextClose;
-    private boolean mIgnoreNextCollapse;
-
-    private boolean mSearchExpanded;
-
-    private RootsCache mRoots;
+    private SearchManager mSearchManager;
     private State mState;
+    private ItemSelectedListener mStackListener;
+    private BaseAdapter mStackAdapter;
+
+    public StandaloneActivity() {
+        super(TAG);
+    }
 
     @Override
     public void onCreate(Bundle icicle) {
-        // Debug.waitForDebugger();
         super.onCreate(icicle);
 
-        mRoots = DocumentsApplication.getRootsCache(this);
-
         setResult(Activity.RESULT_CANCELED);
         setContentView(R.layout.activity);
 
         final Context context = this;
-        final Resources res = getResources();
 
         // Strongly define our horizontal dimension; we leave vertical as
         final WindowManager.LayoutParams a = getWindow().getAttributes();
@@ -131,16 +92,17 @@
 
         mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
 
-        if (icicle != null) {
-            mState = icicle.getParcelable(EXTRA_STATE);
-        } else {
-            buildDefaultState();
-        }
+        mState = (icicle != null)
+            ? icicle.<State>getParcelable(EXTRA_STATE)
+            : buildDefaultState();
 
+        mSearchManager = new SearchManager();
         mToolbar = (Toolbar) findViewById(R.id.toolbar);
         mToolbar.setTitleTextAppearance(context,
                 android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
 
+        mStackAdapter = new StackAdapter();
+        mStackListener = new ItemSelectedListener();
         mToolbarStack = (Spinner) findViewById(R.id.stack);
         mToolbarStack.setOnItemSelectedListener(mStackListener);
 
@@ -167,87 +129,33 @@
         }
     }
 
-    private void buildDefaultState() {
-        mState = new State();
+    private State buildDefaultState() {
+        State state = new State();
 
         final Intent intent = getIntent();
-        mState.action = State.ACTION_BROWSE_ALL;
-        mState.acceptMimes = new String[] { "*/*" };
-        mState.allowMultiple = true;
-        mState.acceptMimes = new String[] { intent.getType() };
-        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
-        mState.showAdvanced = mState.forceAdvanced
+        state.action = State.ACTION_BROWSE_ALL;
+        state.acceptMimes = new String[] { "*/*" };
+        state.allowMultiple = true;
+        state.acceptMimes = new String[] { intent.getType() };
+        state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        state.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = state.forceAdvanced
                 | LocalPreferences.getDisplayAdvancedDevices(this);
-        mState.showSize = true;
+        state.showSize = true;
         final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
         if (stack != null)
-            mState.stack = stack;
-    }
+            state.stack = stack;
 
-    private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            // Restore last stack for calling package
-            final String packageName = getCallingPackageMaybeExtra();
-            final Cursor cursor = getContentResolver()
-                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
-            try {
-                if (cursor.moveToFirst()) {
-                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, mState.stack);
-                    mRestoredStack = true;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to resume: " + e);
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            if (mRestoredStack) {
-                // Update the restored stack to ensure we have freshest data
-                final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Failed to restore stack: " + e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            if (isDestroyed()) return;
-            mState.restored = true;
-            onCurrentDirectoryChanged(ANIM_NONE);
-        }
+        return state;
     }
 
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
-        if (mDrawerToggle != null) {
-            mDrawerToggle.syncState();
-        }
         updateActionBar();
     }
 
     @Override
-    public void setRootsDrawerOpen(boolean open) {
-        Log.w(TAG, "Trying to change state of roots drawer to > " + (open ? "open" : "closed"));
-      // throw new UnsupportedOperationException();
-    }
-
     public void updateActionBar() {
         final RootInfo root = getCurrentRoot();
         mToolbar.setNavigationIcon(
@@ -255,7 +163,7 @@
         mToolbar.setNavigationContentDescription(R.string.drawer_open);
         mToolbar.setNavigationOnClickListener(null);
 
-        if (mSearchExpanded) {
+        if (mSearchManager.isExpanded()) {
             mToolbar.setTitle(null);
             mToolbarStack.setVisibility(View.GONE);
             mToolbarStack.setAdapter(null);
@@ -269,7 +177,7 @@
                 mToolbarStack.setVisibility(View.VISIBLE);
                 mToolbarStack.setAdapter(mStackAdapter);
 
-                mIgnoreNextNavigation = true;
+                mStackListener.mIgnoreNextNavigation = true;
                 mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
             }
         }
@@ -277,220 +185,60 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
+        boolean showMenu = super.onCreateOptionsMenu(menu);
         getMenuInflater().inflate(R.menu.activity, menu);
 
-        for (int i = 0; i < menu.size(); i++) {
-            final MenuItem item = menu.getItem(i);
-            switch (item.getItemId()) {
-                case R.id.menu_advanced:
-                case R.id.menu_file_size:
-                    break;
-                default:
-                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-            }
-        }
+        expandMenus(menu);
 
-        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
-        mSearchView = (SearchView) searchMenu.getActionView();
-        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
-            @Override
-            public boolean onQueryTextSubmit(String query) {
-                mSearchExpanded = true;
-                mState.currentSearch = query;
-                mSearchView.clearFocus();
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
+        this.mSearchManager.install(menu.findItem(R.id.menu_search));
 
-            @Override
-            public boolean onQueryTextChange(String newText) {
-                return false;
-            }
-        });
-
-        searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
-            @Override
-            public boolean onMenuItemActionExpand(MenuItem item) {
-                mSearchExpanded = true;
-                updateActionBar();
-                return true;
-            }
-
-            @Override
-            public boolean onMenuItemActionCollapse(MenuItem item) {
-                mSearchExpanded = false;
-                if (mIgnoreNextCollapse) {
-                    mIgnoreNextCollapse = false;
-                    return true;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return true;
-            }
-        });
-
-        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
-            @Override
-            public boolean onClose() {
-                mSearchExpanded = false;
-                if (mIgnoreNextClose) {
-                    mIgnoreNextClose = false;
-                    return false;
-                }
-
-                mState.currentSearch = null;
-                onCurrentDirectoryChanged(ANIM_NONE);
-                return false;
-            }
-        });
-
-        return true;
+        return showMenu;
     }
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
-        final FragmentManager fm = getFragmentManager();
-
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
         final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
-        final MenuItem search = menu.findItem(R.id.menu_search);
         final MenuItem sort = menu.findItem(R.id.menu_sort);
         final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
         final MenuItem grid = menu.findItem(R.id.menu_grid);
         final MenuItem list = menu.findItem(R.id.menu_list);
         final MenuItem advanced = menu.findItem(R.id.menu_advanced);
         final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+        final MenuItem settings = menu.findItem(R.id.menu_settings);
 
-        sort.setVisible(cwd != null);
         grid.setVisible(mState.derivedMode != State.MODE_GRID);
         list.setVisible(mState.derivedMode != State.MODE_LIST);
 
-        if (mState.currentSearch != null) {
-            // Search uses backend ranking; no sorting
-            sort.setVisible(false);
-
-            search.expandActionView();
-
-            mSearchView.setIconified(false);
-            mSearchView.clearFocus();
-            mSearchView.setQuery(mState.currentSearch, false);
-        } else {
-            mIgnoreNextClose = true;
-            mSearchView.setIconified(true);
-            mSearchView.clearFocus();
-
-            mIgnoreNextCollapse = true;
-            search.collapseActionView();
-        }
+        mSearchManager.update(root);
+        sort.setVisible(cwd != null && !mSearchManager.isSearching());
 
         // Only sort by size when visible
         sortSize.setVisible(mState.showSize);
 
-        fileSize.setVisible(true);
-        search.setVisible(true);
-        createDir.setVisible(true);
-        advanced.setVisible(true);
+        createDir.setVisible(cwd != null
+                && cwd.isCreateSupported()
+                && !mSearchManager.isSearching()
+                && !root.isDownloads());
+
+        fileSize.setVisible(cwd != null);
+        advanced.setVisible(cwd != null);
 
         advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
         fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
                 ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
 
+        settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
 
         return true;
     }
 
     @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
-            return true;
-        }
-
-        final int id = item.getItemId();
-        if (id == android.R.id.home) {
-            onBackPressed();
-            return true;
-        } else if (id == R.id.menu_create_dir) {
-            CreateDirectoryFragment.show(getFragmentManager());
-            return true;
-        } else if (id == R.id.menu_search) {
-            return false;
-        } else if (id == R.id.menu_sort_name) {
-            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
-            return true;
-        } else if (id == R.id.menu_sort_date) {
-            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
-            return true;
-        } else if (id == R.id.menu_sort_size) {
-            setUserSortOrder(State.SORT_ORDER_SIZE);
-            return true;
-        } else if (id == R.id.menu_grid) {
-            setUserMode(State.MODE_GRID);
-            return true;
-        } else if (id == R.id.menu_list) {
-            setUserMode(State.MODE_LIST);
-            return true;
-        } else if (id == R.id.menu_advanced) {
-            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
-            return true;
-        } else if (id == R.id.menu_file_size) {
-            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
-            return true;
-        } else {
-            return super.onOptionsItemSelected(item);
-        }
-    }
-
-    private void setDisplayAdvancedDevices(boolean display) {
-        LocalPreferences.setDisplayAdvancedDevices(this, display);
-        mState.showAdvanced = mState.forceAdvanced | display;
-        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    private void setDisplayFileSize(boolean display) {
-        LocalPreferences.setDisplayFileSize(this, display);
-        mState.showSize = display;
-        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    public void onStateChanged() {
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Set state sort order based on explicit user action.
-     */
-    private void setUserSortOrder(int sortOrder) {
-        mState.userSortOrder = sortOrder;
-        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
-    }
-
-    /**
-     * Set state mode based on explicit user action.
-     */
-    private void setUserMode(int mode) {
-        mState.userMode = mode;
-        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
-    }
-
-    @Override
-    public void setPending(boolean pending) {
-        final SaveFragment save = SaveFragment.get(getFragmentManager());
-        if (save != null) {
-            save.setPending(pending);
-        }
-    }
-
-    @Override
     public void onBackPressed() {
         if (!mState.stackTouched) {
             super.onBackPressed();
@@ -507,130 +255,12 @@
     }
 
     @Override
-    protected void onSaveInstanceState(Bundle state) {
-        super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, mState);
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Bundle state) {
-        super.onRestoreInstanceState(state);
-    }
-
-    private BaseAdapter mStackAdapter = new BaseAdapter() {
-        @Override
-        public int getCount() {
-            return mState.stack.size();
-        }
-
-        @Override
-        public DocumentInfo getItem(int position) {
-            return mState.stack.get(mState.stack.size() - position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir_title, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-            } else {
-                title.setText(doc.displayName);
-            }
-
-            return convertView;
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_subdir, parent, false);
-            }
-
-            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            final DocumentInfo doc = getItem(position);
-
-            if (position == 0) {
-                final RootInfo root = getCurrentRoot();
-                title.setText(root.title);
-                subdir.setVisibility(View.GONE);
-            } else {
-                title.setText(doc.displayName);
-                subdir.setVisibility(View.VISIBLE);
-            }
-
-            return convertView;
-        }
-    };
-
-    private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            if (mIgnoreNextNavigation) {
-                mIgnoreNextNavigation = false;
-                return;
-            }
-
-            while (mState.stack.size() > position + 1) {
-                mState.stackTouched = true;
-                mState.stack.pop();
-            }
-            onCurrentDirectoryChanged(ANIM_UP);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> parent) {
-            // Ignored
-        }
-    };
-
-    @Override
-    public RootInfo getCurrentRoot() {
-        if (mState.stack.root != null) {
-            return mState.stack.root;
-        } else {
-            return mRoots.getRecentsRoot();
-        }
-    }
-
-    public DocumentInfo getCurrentDirectory() {
-        return mState.stack.peek();
-    }
-
-    private String getCallingPackageMaybeExtra() {
-        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
-        return (extra != null) ? extra : getCallingPackage();
-    }
-
-    public Executor getCurrentExecutor() {
-        final DocumentInfo cwd = getCurrentDirectory();
-        if (cwd != null && cwd.authority != null) {
-            return ProviderExecutor.forAuthority(cwd.authority);
-        } else {
-            return AsyncTask.THREAD_POOL_EXECUTOR;
-        }
-    }
-
-    @Override
     public State getDisplayState() {
         return mState;
     }
 
-    private void onCurrentDirectoryChanged(int anim) {
+    @Override
+    void onDirectoryChanged(int anim) {
         final FragmentManager fm = getFragmentManager();
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
@@ -654,109 +284,10 @@
                 DirectoryFragment.showNormal(fm, root, cwd, anim);
             }
         }
-
-        final RootsFragment roots = RootsFragment.get(fm);
-        if (roots != null) {
-            roots.onCurrentRootChanged();
-        }
-
-        updateActionBar();
-        invalidateOptionsMenu();
-        dumpStack();
-    }
-
-    @Override
-    public void onStackPicked(DocumentStack stack) {
-        try {
-            // Update the restored stack to ensure we have freshest data
-            stack.updateDocuments(getContentResolver());
-
-            mState.stack = stack;
-            mState.stackTouched = true;
-            onCurrentDirectoryChanged(ANIM_SIDE);
-
-        } catch (FileNotFoundException e) {
-            Log.w(TAG, "Failed to restore stack: " + e);
-        }
-    }
-
-    @Override
-    public void onRootPicked(RootInfo root, boolean closeDrawer) {
-        // Clear entire backstack and start in new root
-        mState.stack.root = root;
-        mState.stack.clear();
-        mState.stackTouched = true;
-
-        if (!mRoots.isRecentsRoot(root)) {
-            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
-        } else {
-            onCurrentDirectoryChanged(ANIM_SIDE);
-        }
-    }
-
-    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
-        private RootInfo mRoot;
-
-        public PickRootTask(RootInfo root) {
-            mRoot = root;
-        }
-
-        @Override
-        protected DocumentInfo doInBackground(Void... params) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(
-                        mRoot.authority, mRoot.documentId);
-                return DocumentInfo.fromUri(getContentResolver(), uri);
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Failed to find root", e);
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPostExecute(DocumentInfo result) {
-            if (result != null) {
-                mState.stack.push(result);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            }
-        }
-    }
-
-    @Override
-    public void onAppPicked(ResolveInfo info) {
-        final Intent intent = new Intent(getIntent());
-        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-        intent.setComponent(new ComponentName(
-                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
-        startActivityForResult(intent, CODE_FORWARD);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        Log.d(TAG, "onActivityResult() code=" + resultCode);
-
-        // Only relay back results when not canceled; otherwise stick around to
-        // let the user pick another app/backend.
-        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
-
-            // Remember that we last picked via external app
-            final String packageName = getCallingPackageMaybeExtra();
-            final ContentValues values = new ContentValues();
-            values.put(ResumeColumns.EXTERNAL, 1);
-            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
-
-            // Pass back result to original caller
-            setResult(resultCode, data);
-            finish();
-        } else {
-            super.onActivityResult(requestCode, resultCode, data);
-        }
     }
 
     @Override
     public void onDocumentPicked(DocumentInfo doc) {
-        final FragmentManager fm = getFragmentManager();
         if (doc.isDirectory()) {
             mState.stack.push(doc);
             mState.stackTouched = true;
@@ -780,27 +311,12 @@
     }
 
     @Override
-    public void onSaveRequested(DocumentInfo replaceTarget) {
-        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onSaveRequested(String mimeType, String displayName) {
-        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
-    }
-
-    @Override
-    public void onPickRequested(DocumentInfo pickTarget) {
-        final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
-                pickTarget.documentId);
-        new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
-    }
-
-    private void saveStackBlocking() {
+    void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+        final byte[] rawStack = DurableUtils.writeToArrayOrNull(
+                getDisplayState().stack);
 
         // Remember location for next app launch
         final String packageName = getCallingPackageMaybeExtra();
@@ -810,7 +326,8 @@
         resolver.insert(RecentsProvider.buildResume(packageName), values);
     }
 
-    private void onFinished(Uri... uris) {
+    @Override
+    void onTaskFinished(Uri... uris) {
         Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
@@ -832,106 +349,4 @@
         setResult(Activity.RESULT_OK, intent);
         finish();
     }
-
-    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
-        private final String mMimeType;
-        private final String mDisplayName;
-
-        public CreateFinishTask(String mimeType, String displayName) {
-            mMimeType = mimeType;
-            mDisplayName = displayName;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            setPending(true);
-        }
-
-        @Override
-        protected Uri doInBackground(Void... params) {
-            final ContentResolver resolver = getContentResolver();
-            final DocumentInfo cwd = getCurrentDirectory();
-
-            ContentProviderClient client = null;
-            Uri childUri = null;
-            try {
-                client = DocumentsApplication.acquireUnstableProviderOrThrow(
-                        resolver, cwd.derivedUri.getAuthority());
-                childUri = DocumentsContract.createDocument(
-                        client, cwd.derivedUri, mMimeType, mDisplayName);
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to create document", e);
-            } finally {
-                ContentProviderClient.releaseQuietly(client);
-            }
-
-            if (childUri != null) {
-                saveStackBlocking();
-            }
-
-            return childUri;
-        }
-
-        @Override
-        protected void onPostExecute(Uri result) {
-            if (result != null) {
-                onFinished(result);
-            } else {
-                Toast.makeText(StandaloneActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
-                        .show();
-            }
-
-            setPending(false);
-        }
-    }
-
-    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri[] mUris;
-
-        public ExistingFinishTask(Uri... uris) {
-            mUris = uris;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUris);
-        }
-    }
-
-    private class PickFinishTask extends AsyncTask<Void, Void, Void> {
-        private final Uri mUri;
-
-        public PickFinishTask(Uri uri) {
-            mUri = uri;
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            saveStackBlocking();
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Void result) {
-            onFinished(mUri);
-        }
-    }
-
-    private void dumpStack() {
-        Log.d(TAG, "Current stack: ");
-        Log.d(TAG, " * " + mState.stack.root);
-        for (DocumentInfo doc : mState.stack) {
-            Log.d(TAG, " +-- " + doc);
-        }
-    }
-
-    public static BaseActivity get(Fragment fragment) {
-        return (BaseActivity) fragment.getActivity();
-    }
 }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9f80fd8..5d02576 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -460,6 +460,7 @@
     // set to null if in idle mode; while in this mode, any alarms we don't want
     // to run during this time are placed in mPendingWhileIdleAlarms
     Alarm mPendingIdleUntil = null;
+    Alarm mNextWakeFromIdle = null;
     final ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
 
     public AlarmManagerService(Context context) {
@@ -583,7 +584,7 @@
         final int mAlarmType;
 
         InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
-                int alarmType, String tag) {
+                int alarmType, String tag, long nowELAPSED) {
             mPendingIntent = pendingIntent;
             mWorkSource = workSource;
             mTag = tag;
@@ -593,6 +594,7 @@
                 fs = new FilterStats(mBroadcastStats, mTag);
                 mBroadcastStats.filterStats.put(mTag, fs);
             }
+            fs.lastTime = nowELAPSED;
             mFilterStats = fs;
             mAlarmType = alarmType;
         }
@@ -602,6 +604,7 @@
         final BroadcastStats mBroadcastStats;
         final String mTag;
 
+        long lastTime;
         long aggregateTime;
         int count;
         int numWakeup;
@@ -806,33 +809,38 @@
         setImplLocked(a, false, doValidate);
     }
 
+    private void updateNextWakeFromIdleFuzzLocked() {
+        if (mNextWakeFromIdle != null) {
+
+        }
+    }
+
     private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
-            // This is a special alarm that will put the system idle until it goes off.
+            // This is a special alarm that will put the system into idle until it goes off.
             // The caller has given the time they want this to happen at, however we need
             // to pull that earlier if there are existing alarms that have requested to
             // bring us out of idle.
-            final int N = mAlarmBatches.size();
-            for (int i = 0; i < N; i++) {
-                Batch b = mAlarmBatches.get(i);
-                if (a.whenElapsed > b.end) {
-                    // There are no interesting things happening before our idle until,
-                    // so keep the requested time.
-                    break;
-                }
-                if ((b.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
-                    a.when = a.whenElapsed = a.maxWhenElapsed = b.end;
-                    break;
-                }
+            if (mNextWakeFromIdle != null) {
+                a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
             }
             // Add fuzz to make the alarm go off some time before the actual desired time.
             final long nowElapsed = SystemClock.elapsedRealtime();
-            long fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
+            final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
             if (fuzz > 0) {
                 if (mRandom == null) {
                     mRandom = new Random();
                 }
-                a.whenElapsed -= mRandom.nextLong() % fuzz;
+                final int delta = mRandom.nextInt(fuzz);
+                a.whenElapsed -= delta;
+                if (false) {
+                    Slog.d(TAG, "Alarm when: " + a.whenElapsed);
+                    Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed));
+                    Slog.d(TAG, "Applied fuzz: " + fuzz);
+                    Slog.d(TAG, "Final delta: " + delta);
+                    Slog.d(TAG, "Final when: " + a.whenElapsed);
+                }
+                a.when = a.maxWhenElapsed = a.whenElapsed;
             }
 
         } else if (mPendingIdleUntil != null) {
@@ -869,11 +877,16 @@
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
             mPendingIdleUntil = a;
             needRebatch = true;
-        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0 && mPendingIdleUntil != null) {
-            // If we are adding an alarm that asks to wake from idle, and we are currently
-            // idling, then we need to rebatch alarms in case the idle until time needs to
-            // be updated.
-            needRebatch = true;
+        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+            if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+                mNextWakeFromIdle = a;
+                // If this wake from idle is earlier than whatever was previously scheduled,
+                // and we are currently idling, then we need to rebatch alarms in case the idle
+                // until time needs to be updated.
+                if (mPendingIdleUntil != null) {
+                    needRebatch = true;
+                }
+            }
         }
 
         if (!rebatching) {
@@ -960,6 +973,11 @@
         }
 
         @Override
+        public long getNextWakeFromIdleTime() {
+            return getNextWakeFromIdleTimeImpl();
+        }
+
+        @Override
         public AlarmManager.AlarmClockInfo getNextAlarmClock(int userId) {
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, false /* allowAll */, false /* requireFull */,
@@ -1030,10 +1048,15 @@
                 pw.println();
                 pw.println("Idle mode state:");
                 pw.print("  Idling until: "); pw.println(mPendingIdleUntil);
-                mPendingIdleUntil.dump(pw, "    ", nowELAPSED, nowRTC, sdf);
+                mPendingIdleUntil.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
                 pw.println("  Pending alarms:");
                 dumpAlarmList(pw, mPendingWhileIdleAlarms, "    ", nowELAPSED, nowRTC, sdf);
             }
+            if (mNextWakeFromIdle != null) {
+                pw.println();
+                pw.print("  Next wake from idle: "); pw.println(mNextWakeFromIdle);
+                mNextWakeFromIdle.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
+            }
 
             pw.println();
             pw.print("Past-due non-wakeup alarms: ");
@@ -1140,7 +1163,10 @@
                                 TimeUtils.formatDuration(fs.aggregateTime, pw);
                                 pw.print(" "); pw.print(fs.numWakeup);
                                 pw.print(" wakes " ); pw.print(fs.count);
-                                pw.print(" alarms: ");
+                                pw.print(" alarms, last ");
+                                TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
+                                pw.println(":");
+                        pw.print("      ");
                                 pw.print(fs.mTag);
                                 pw.println();
                     }
@@ -1216,7 +1242,13 @@
         return null;
     }
 
-    private AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
+    long getNextWakeFromIdleTimeImpl() {
+        synchronized (mLock) {
+            return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+        }
+    }
+
+    AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
         synchronized (mLock) {
             return mNextAlarmClockForUser.get(userId);
         }
@@ -1398,6 +1430,9 @@
                 mPendingIdleUntil = null;
                 restorePending = true;
             }
+            if (mNextWakeFromIdle != null && mNextWakeFromIdle.operation.equals(operation)) {
+                mNextWakeFromIdle = null;
+            }
             rebatchAllAlarmsLocked(true);
             if (restorePending) {
                 restorePendingWhileIdleAlarmsLocked();
@@ -1585,11 +1620,19 @@
                 Alarm alarm = batch.get(i);
                 alarm.count = 1;
                 triggerList.add(alarm);
+                if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+                    EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
+                            alarm.tag);
+                }
                 if (mPendingIdleUntil == alarm) {
                     mPendingIdleUntil = null;
                     rebatchAllAlarmsLocked(false);
                     restorePendingWhileIdleAlarmsLocked();
                 }
+                if (mNextWakeFromIdle == alarm) {
+                    mNextWakeFromIdle = null;
+                    rebatchAllAlarmsLocked(false);
+                }
 
                 // Recurring alarms may have passed several alarm intervals while the
                 // phone was asleep or off, so pass a trigger count when sending them.
@@ -1655,7 +1698,7 @@
         public final long origWhen;
         public final boolean wakeup;
         public final PendingIntent operation;
-        public final String  tag;
+        public final String tag;
         public final WorkSource workSource;
         public final int flags;
         public int count;
@@ -1725,6 +1768,12 @@
                     pw.print(" repeatInterval="); pw.print(repeatInterval);
                     pw.print(" count="); pw.print(count);
                     pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
+            if (alarmClock != null) {
+                pw.print(prefix); pw.println("Alarm clock:");
+                pw.print(prefix); pw.print("  triggerTime=");
+                pw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
+                pw.print(prefix); pw.print("  showIntent="); pw.println(alarmClock.getShowIntent());
+            }
             pw.print(prefix); pw.print("operation="); pw.println(operation);
         }
     }
@@ -1762,11 +1811,11 @@
         }
     }
 
-    static long fuzzForDuration(long duration) {
+    static int fuzzForDuration(long duration) {
         if (duration < 15*60*1000) {
             // If the duration until the time is less than 15 minutes, the maximum fuzz
             // is the duration.
-            return duration;
+            return (int)duration;
         } else if (duration < 90*60*1000) {
             // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
             return 15*60*1000;
@@ -1824,7 +1873,7 @@
                     mWakeLock.acquire();
                 }
                 final InFlight inflight = new InFlight(AlarmManagerService.this,
-                        alarm.operation, alarm.workSource, alarm.type, alarm.tag);
+                        alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
                 mInFlight.add(inflight);
                 mBroadcastRefCount++;
 
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index a227341..abd2ca0 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -173,6 +173,13 @@
 # ---------------------------
 33000 wp_wallpaper_crashed (component|3)
 
+# ---------------------------
+# Device idle
+# ---------------------------
+34000 device_idle (state|1|5), (reason|3)
+34001 device_idle_step
+34002 device_idle_wake_from_idle (is_idle|1|5), (reason|3)
+
 
 # ---------------------------
 # ConnectivityService.java
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index a72c77e..d3240ec 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -550,14 +550,19 @@
             }
         }
 
-        try {
-            // Convert properties to string contents and send it to HAL.
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
-            properties.store(baos, null);
-            native_configuration_update(baos.toString());
-            Log.d(TAG, "final config = " + baos.toString());
-        } catch (IOException ex) {
-            Log.w(TAG, "failed to dump properties contents");
+        if (native_is_gnss_configuration_supported()) {
+            try {
+                // Convert properties to string contents and send it to HAL.
+                ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
+                properties.store(baos, null);
+                native_configuration_update(baos.toString());
+                Log.d(TAG, "final config = " + baos.toString());
+            } catch (IOException ex) {
+                Log.w(TAG, "failed to dump properties contents");
+            }
+        } else if (DEBUG) {
+            Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+                    + " supported");
         }
 
         // SUPL_ES configuration.
@@ -732,16 +737,21 @@
         }
 
         if (info != null) {
-            boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
-            boolean networkAvailable = info.isAvailable() && dataEnabled;
-            String defaultApn = getSelectedApn();
-            if (defaultApn == null) {
-                defaultApn = "dummy-apn";
-            }
+            if (native_is_agps_ril_supported()) {
+                boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+                boolean networkAvailable = info.isAvailable() && dataEnabled;
+                String defaultApn = getSelectedApn();
+                if (defaultApn == null) {
+                    defaultApn = "dummy-apn";
+                }
 
-            native_update_network_state(info.isConnected(), info.getType(),
-                                        info.isRoaming(), networkAvailable,
-                                        info.getExtraInfo(), defaultApn);
+                native_update_network_state(info.isConnected(), info.getType(),
+                        info.isRoaming(), networkAvailable,
+                        info.getExtraInfo(), defaultApn);
+            } else if (DEBUG) {
+                Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
+                        + " supported");
+            }
         }
 
         if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
@@ -1752,7 +1762,7 @@
     // NI Client support
     //=============================================================
     private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
-        // Sends a response for an NI reqeust to HAL.
+        // Sends a response for an NI request to HAL.
         @Override
         public boolean sendNiResponse(int notificationId, int userResponse)
         {
@@ -1843,7 +1853,7 @@
     private void requestSetID(int flags) {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        int    type = AGPS_SETID_TYPE_NONE;
+        int type = AGPS_SETID_TYPE_NONE;
         String data = "";
 
         if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
@@ -1994,20 +2004,26 @@
                     .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
 
             // listen for events
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
-            intentFilter.addDataScheme("sms");
-            intentFilter.addDataAuthority("localhost","7275");
-            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+            IntentFilter intentFilter;
+            if (native_is_agps_ril_supported()) {
+                intentFilter = new IntentFilter();
+                intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
+                intentFilter.addDataScheme("sms");
+                intentFilter.addDataAuthority("localhost", "7275");
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
-            intentFilter = new IntentFilter();
-            intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
-            try {
-                intentFilter.addDataType("application/vnd.omaloc-supl-init");
-            } catch (IntentFilter.MalformedMimeTypeException e) {
-                Log.w(TAG, "Malformed SUPL init mime type");
+                intentFilter = new IntentFilter();
+                intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
+                try {
+                    intentFilter.addDataType("application/vnd.omaloc-supl-init");
+                } catch (IntentFilter.MalformedMimeTypeException e) {
+                    Log.w(TAG, "Malformed SUPL init mime type");
+                }
+                mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+            } else if (DEBUG) {
+                Log.d(TAG, "Skipped registration for SMS/WAP-PUSH messages because AGPS Ril in GPS"
+                        + " HAL is not supported");
             }
-            mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
             intentFilter = new IntentFilter();
             intentFilter.addAction(ALARM_WAKEUP);
@@ -2187,6 +2203,8 @@
     static { class_init_native(); }
     private static native void class_init_native();
     private static native boolean native_is_supported();
+    private static native boolean native_is_agps_ril_supported();
+    private static native boolean native_is_gnss_configuration_supported();
 
     private native boolean native_init();
     private native void native_cleanup();
diff --git a/services/core/java/com/android/server/power/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
index a23a87b..6b29b9a 100644
--- a/services/core/java/com/android/server/power/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -41,6 +41,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.SystemService;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.EventLogTags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -100,6 +101,11 @@
      * Scaling factor to apply to current idle timeout each time we cycle through that state.
      */
     private static final float DEFAULT_IDLE_FACTOR = 2f;
+    /**
+     * This is the minimum time we will allow until the next upcoming alarm for us to
+     * actually go in to idle mode.
+     */
+    private static final long DEFAULT_MIN_TIME_TO_ALARM = 60*60*1000L;
 
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
@@ -239,7 +245,7 @@
             becomeInactiveIfAppropriateLocked();
         } else if (screenOn) {
             mScreenOn = true;
-            becomeActiveLocked();
+            becomeActiveLocked("screen");
         }
     }
 
@@ -249,12 +255,13 @@
             becomeInactiveIfAppropriateLocked();
         } else if (charging) {
             mCharging = charging;
-            becomeActiveLocked();
+            becomeActiveLocked("charging");
         }
     }
 
-    void becomeActiveLocked() {
+    void becomeActiveLocked(String reason) {
         if (mState != STATE_ACTIVE) {
+            EventLogTags.writeDeviceIdle(STATE_ACTIVE, reason);
             mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -281,10 +288,22 @@
             mNextIdlePendingDelay = 0;
             mNextIdleDelay = 0;
             scheduleAlarmLocked(mInactiveTimeout, false);
+            EventLogTags.writeDeviceIdle(mState, "no activity");
         }
     }
 
     void stepIdleStateLocked() {
+        EventLogTags.writeDeviceIdleStep();
+
+        final long now = SystemClock.elapsedRealtime();
+        if ((now+DEFAULT_MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
+            // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
+            if (mState != STATE_ACTIVE) {
+                becomeActiveLocked("alarm");
+            }
+            return;
+        }
+
         switch (mState) {
             case STATE_INACTIVE:
                 // We have now been inactive long enough, it is time to start looking
@@ -295,6 +314,7 @@
                 mNextIdlePendingDelay = DEFAULT_IDLE_PENDING_TIMEOUT;
                 mNextIdleDelay = DEFAULT_IDLE_TIMEOUT;
                 mState = STATE_IDLE_PENDING;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 break;
             case STATE_IDLE_PENDING:
                 // We have been waiting to become idle, and now it is time!  This is the
@@ -307,6 +327,7 @@
                     mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
                 }
                 mState = STATE_IDLE;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 mLocalPowerManager.setDeviceIdleMode(true);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(true);
@@ -323,6 +344,7 @@
                     mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
                 }
                 mState = STATE_IDLE_PENDING;
+                EventLogTags.writeDeviceIdle(mState, "step");
                 mLocalPowerManager.setDeviceIdleMode(false);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(false);
@@ -352,6 +374,7 @@
             }
             mState = STATE_ACTIVE;
             mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+            EventLogTags.writeDeviceIdle(mState, "motion");
             becomeInactiveIfAppropriateLocked();
         }
     }
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 0cd6eb5..3804e1d 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -509,13 +509,22 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* /* env */,
-                                                                  jclass /* clazz */) {
-    if (sGpsInterface != NULL) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
+static jboolean android_location_GpsLocationProvider_is_supported(
+        JNIEnv* /* env */, jclass /* clazz */)
+{
+    return (sGpsInterface != NULL) ?  JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_is_agps_ril_supported(
+        JNIEnv* /* env */, jclass /* clazz */)
+{
+    return (sAGpsRilInterface != NULL) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
+        JNIEnv* /* env */, jclass /* jclazz */)
+{
+    return (sGnssConfigurationInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
@@ -715,14 +724,10 @@
         sGpsInterface->inject_location(latitude, longitude, accuracy);
 }
 
-static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* /* env */,
-                                                                   jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_supports_xtra(
+        JNIEnv* /* env */, jobject /* obj */)
 {
-    if (sGpsXtraInterface != NULL) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
+    return (sGpsXtraInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject /* obj */,
@@ -844,13 +849,10 @@
     }
 }
 
-static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* /* env */,
-                                                                           jobject /* obj */)
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(
+        JNIEnv* /* env */, jobject /* obj */)
 {
-    if (sGpsGeofencingInterface != NULL) {
-        return JNI_TRUE;
-    }
-    return JNI_FALSE;
+    return (sGpsGeofencingInterface != NULL) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* /* env */,
@@ -1436,6 +1438,10 @@
      /* name, signature, funcPtr */
     {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
     {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
+    {"native_is_agps_ril_supported", "()Z",
+            (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
+    {"native_is_gnss_configuration_supported", "()Z",
+            (void*)android_location_gpsLocationProvider_is_gnss_configuration_supported},
     {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
     {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
     {"native_set_position_mode",
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 17db3fb..7b58755 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -470,8 +470,6 @@
 
     /**
      * Get signal level as an int from 0..4
-     *
-     * @hide
      */
     public int getLevel() {
         int level;