Merge "Removed bugreport intents from protected list." into nyc-dev
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8717353..837ceb6 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -268,154 +268,155 @@
                 return mClassLoader;
             }
 
-            if (mIncludeCode && !mPackageName.equals("android")) {
-                // Avoid the binder call when the package is the current application package.
-                // The activity manager will perform ensure that dexopt is performed before
-                // spinning up the process.
-                if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
-                    final String isa = VMRuntime.getRuntime().vmInstructionSet();
-                    try {
-                        ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
-                    } catch (RemoteException re) {
-                        // Ignored.
-                    }
-                }
-
-                final List<String> zipPaths = new ArrayList<>();
-                final List<String> apkPaths = new ArrayList<>();
-                final List<String> libPaths = new ArrayList<>();
-
-                if (mRegisterPackage) {
-                    try {
-                        ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
-                    } catch (RemoteException e) {
-                    }
-                }
-
-                zipPaths.add(mAppDir);
-                if (mSplitAppDirs != null) {
-                    Collections.addAll(zipPaths, mSplitAppDirs);
-                }
-
-                libPaths.add(mLibDir);
-
-                /*
-                 * The following is a bit of a hack to inject
-                 * instrumentation into the system: If the app
-                 * being started matches one of the instrumentation names,
-                 * then we combine both the "instrumentation" and
-                 * "instrumented" app into the path, along with the
-                 * concatenation of both apps' shared library lists.
-                 */
-
-                String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
-                String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
-                String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
-                String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
-
-                String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
-                String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
-                String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
-                String[] instrumentationLibs = null;
-
-                if (mAppDir.equals(instrumentationAppDir)
-                        || mAppDir.equals(instrumentedAppDir)) {
-                    zipPaths.clear();
-                    zipPaths.add(instrumentationAppDir);
-                    if (instrumentationSplitAppDirs != null) {
-                        Collections.addAll(zipPaths, instrumentationSplitAppDirs);
-                    }
-                    zipPaths.add(instrumentedAppDir);
-                    if (instrumentedSplitAppDirs != null) {
-                        Collections.addAll(zipPaths, instrumentedSplitAppDirs);
-                    }
-
-                    libPaths.clear();
-                    libPaths.add(instrumentationLibDir);
-                    libPaths.add(instrumentedLibDir);
-
-                    if (!instrumentedAppDir.equals(instrumentationAppDir)) {
-                        instrumentationLibs = getLibrariesFor(instrumentationPackageName);
-                    }
-                }
-
-                apkPaths.addAll(zipPaths);
-
-                if (mSharedLibraries != null) {
-                    for (String lib : mSharedLibraries) {
-                        if (!zipPaths.contains(lib)) {
-                            zipPaths.add(0, lib);
-                        }
-                    }
-                }
-
-                if (instrumentationLibs != null) {
-                    for (String lib : instrumentationLibs) {
-                        if (!zipPaths.contains(lib)) {
-                            zipPaths.add(0, lib);
-                        }
-                    }
-                }
-
-                final String zip = TextUtils.join(File.pathSeparator, zipPaths);
-
-                // Add path to libraries in apk for current abi
-                if (mApplicationInfo.primaryCpuAbi != null) {
-                    for (String apk : apkPaths) {
-                      libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi);
-                    }
-                }
-
-                String libraryPermittedPath = mDataDir;
-                boolean isBundledApp = false;
-
-                if (mApplicationInfo.isSystemApp()) {
-                    isBundledApp = true;
-                    // Add path to system libraries to libPaths;
-                    // Access to system libs should be limited
-                    // to bundled applications; this is why updated
-                    // system apps are not included.
-                    libPaths.add(System.getProperty("java.library.path"));
-
-                    // This is necessary to grant bundled apps access to
-                    // libraries located in subdirectories of /system/lib
-                    libraryPermittedPath += File.pathSeparator +
-                                            System.getProperty("java.library.path");
-                }
-                // DO NOT SHIP: this is a workaround for apps loading native libraries
-                // provided by 3rd party apps using absolute path instead of corresponding
-                // classloader; see http://b/26954419 for example.
-                if (mApplicationInfo.targetSdkVersion <= 23) {
-                    libraryPermittedPath += File.pathSeparator + "/data/app";
-                }
-                // -----------------------------------------------------------------------------
-
-                final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
-
-                /*
-                 * With all the combination done (if necessary, actually
-                 * create the class loader.
-                 */
-
-                if (ActivityThread.localLOGV)
-                    Slog.v(ActivityThread.TAG, "Class path: " + zip +
-                            ", JNI path: " + librarySearchPath);
-
-                // Temporarily disable logging of disk reads on the Looper thread
-                // as this is early and necessary.
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-
-                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
-                        librarySearchPath, libraryPermittedPath, mBaseClassLoader);
-
-                StrictMode.setThreadPolicy(oldPolicy);
-            } else {
+            if (mPackageName.equals("android")) {
                 if (mBaseClassLoader == null) {
                     mClassLoader = ClassLoader.getSystemClassLoader();
                 } else {
                     mClassLoader = mBaseClassLoader;
                 }
+                return mClassLoader;
             }
+
+            // Avoid the binder call when the package is the current application package.
+            // The activity manager will perform ensure that dexopt is performed before
+            // spinning up the process.
+            if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+                final String isa = VMRuntime.getRuntime().vmInstructionSet();
+                try {
+                    ActivityThread.getPackageManager().notifyPackageUse(mPackageName);
+                } catch (RemoteException re) {
+                    // Ignored.
+                }
+            }
+
+            final List<String> zipPaths = new ArrayList<>();
+            final List<String> apkPaths = new ArrayList<>();
+            final List<String> libPaths = new ArrayList<>();
+
+            if (mRegisterPackage) {
+                try {
+                    ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
+                } catch (RemoteException e) {
+                }
+            }
+
+            zipPaths.add(mAppDir);
+            if (mSplitAppDirs != null) {
+                Collections.addAll(zipPaths, mSplitAppDirs);
+            }
+
+            libPaths.add(mLibDir);
+
+            /*
+             * The following is a bit of a hack to inject
+             * instrumentation into the system: If the app
+             * being started matches one of the instrumentation names,
+             * then we combine both the "instrumentation" and
+             * "instrumented" app into the path, along with the
+             * concatenation of both apps' shared library lists.
+             */
+
+            String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
+            String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
+            String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
+            String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
+
+            String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
+            String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
+            String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
+            String[] instrumentationLibs = null;
+
+            if (mAppDir.equals(instrumentationAppDir)
+                    || mAppDir.equals(instrumentedAppDir)) {
+                zipPaths.clear();
+                zipPaths.add(instrumentationAppDir);
+                if (instrumentationSplitAppDirs != null) {
+                    Collections.addAll(zipPaths, instrumentationSplitAppDirs);
+                }
+                zipPaths.add(instrumentedAppDir);
+                if (instrumentedSplitAppDirs != null) {
+                    Collections.addAll(zipPaths, instrumentedSplitAppDirs);
+                }
+
+                libPaths.clear();
+                libPaths.add(instrumentationLibDir);
+                libPaths.add(instrumentedLibDir);
+
+                if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+                    instrumentationLibs = getLibrariesFor(instrumentationPackageName);
+                }
+            }
+
+            apkPaths.addAll(zipPaths);
+
+            if (mSharedLibraries != null) {
+                for (String lib : mSharedLibraries) {
+                    if (!zipPaths.contains(lib)) {
+                        zipPaths.add(0, lib);
+                    }
+                }
+            }
+
+            if (instrumentationLibs != null) {
+                for (String lib : instrumentationLibs) {
+                    if (!zipPaths.contains(lib)) {
+                        zipPaths.add(0, lib);
+                    }
+                }
+            }
+
+            final String zip = mIncludeCode ? TextUtils.join(File.pathSeparator, zipPaths) : "";
+
+            // Add path to libraries in apk for current abi
+            if (mApplicationInfo.primaryCpuAbi != null) {
+                for (String apk : apkPaths) {
+                  libPaths.add(apk + "!/lib/" + mApplicationInfo.primaryCpuAbi);
+                }
+            }
+
+            String libraryPermittedPath = mDataDir;
+            boolean isBundledApp = false;
+
+            if (mApplicationInfo.isSystemApp()) {
+                isBundledApp = true;
+                // Add path to system libraries to libPaths;
+                // Access to system libs should be limited
+                // to bundled applications; this is why updated
+                // system apps are not included.
+                libPaths.add(System.getProperty("java.library.path"));
+
+                // This is necessary to grant bundled apps access to
+                // libraries located in subdirectories of /system/lib
+                libraryPermittedPath += File.pathSeparator +
+                                        System.getProperty("java.library.path");
+            }
+            // DO NOT SHIP: this is a workaround for apps loading native libraries
+            // provided by 3rd party apps using absolute path instead of corresponding
+            // classloader; see http://b/26954419 for example.
+            if (mApplicationInfo.targetSdkVersion <= 23) {
+                libraryPermittedPath += File.pathSeparator + "/data/app";
+            }
+            // -----------------------------------------------------------------------------
+
+            final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
+
+            /*
+             * With all the combination done (if necessary, actually
+             * create the class loader.
+             */
+
+            if (ActivityThread.localLOGV)
+                Slog.v(ActivityThread.TAG, "Class path: " + zip +
+                        ", JNI path: " + librarySearchPath);
+
+            // Temporarily disable logging of disk reads on the Looper thread
+            // as this is early and necessary.
+            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+
+            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, isBundledApp,
+                    librarySearchPath, libraryPermittedPath, mBaseClassLoader);
+
+            StrictMode.setThreadPolicy(oldPolicy);
             return mClassLoader;
         }
     }
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index aa14cb5..3c7f48b 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -166,7 +166,8 @@
         String path = classLoader.findLibrary(libname);
 
         if (path == null) {
-            throw new IllegalArgumentException("Unable to find native library: " + libname);
+            throw new IllegalArgumentException("Unable to find native library " + libname +
+                                               " using classloader: " + classLoader.toString());
         }
         
         byte[] nativeSavedState = savedInstanceState != null
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index 8803c4b..22fbee2 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -44,7 +44,7 @@
 
     public ResourceCertificateSource(int resourceId, Context context) {
         mResourceId = resourceId;
-        mContext = context.getApplicationContext();
+        mContext = context;
     }
 
     private void ensureInitialized() {
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 294edb6..bd71e0d 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,6 +160,10 @@
     }
 
     if (!mStagingRequests.empty()) {
+        // No interpolator was set, use the default
+        if (mPlayState == PlayState::NotStarted && !mInterpolator) {
+            mInterpolator.reset(Interpolator::createDefaultInterpolator());
+        }
         // Keep track of the play state and play time before they are changed when
         // staging requests are resolved.
         nsecs_t currentPlayTime = mPlayTime;
@@ -222,10 +226,6 @@
         // Set to 0 so that the animate() basically instantly finishes
         mStartTime = 0;
     }
-    // No interpolator was set, use the default
-    if (!mInterpolator) {
-        mInterpolator.reset(Interpolator::createDefaultInterpolator());
-    }
     if (mDuration < 0 || mDuration > 50000) {
         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
     }
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 58e7709..637e06e 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -1,6 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.documentsui">
 
+    <uses-permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index b67a6915..a34dd55 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -23,9 +23,11 @@
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
 import static com.android.internal.util.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.Fragment;
+import android.app.FragmentManager;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -46,7 +48,7 @@
 import android.view.MenuItem;
 import android.widget.Spinner;
 
-import com.android.documentsui.SearchManager.SearchManagerListener;
+import com.android.documentsui.SearchViewManager.SearchManagerListener;
 import com.android.documentsui.State.ViewMode;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.dirlist.Model;
@@ -64,14 +66,12 @@
 public abstract class BaseActivity extends Activity
         implements SearchManagerListener, NavigationView.Environment {
 
-    static final String EXTRA_STATE = "state";
-
     // See comments where this const is referenced for details.
     private static final int DRAWER_NO_FIDDLE_DELAY = 1500;
 
     State mState;
     RootsCache mRoots;
-    SearchManager mSearchManager;
+    SearchViewManager mSearchManager;
     DrawerController mDrawer;
     NavigationView mNavigator;
 
@@ -121,7 +121,7 @@
                     }
                 });
 
-        mSearchManager = new SearchManager(this);
+        mSearchManager = new SearchViewManager(this, icicle);
 
         DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
         setActionBar(toolbar);
@@ -141,6 +141,7 @@
         boolean showMenu = super.onCreateOptionsMenu(menu);
 
         getMenuInflater().inflate(R.menu.activity, menu);
+        mNavigator.update();
         mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar));
 
         return showMenu;
@@ -188,7 +189,7 @@
 
     private State getState(@Nullable Bundle icicle) {
         if (icicle != null) {
-            State state = icicle.<State>getParcelable(EXTRA_STATE);
+            State state = icicle.<State>getParcelable(Shared.EXTRA_STATE);
             if (DEBUG) Log.d(mTag, "Recovered existing state object: " + state);
             return state;
         }
@@ -224,6 +225,9 @@
     }
 
     void onRootPicked(RootInfo root) {
+        // Clicking on the current root removes search
+        mSearchManager.cancelSearch();
+
         // Skip refreshing if root nor directory didn't change
         if (root.equals(getCurrentRoot()) && mState.stack.size() == 1) {
             return;
@@ -233,7 +237,6 @@
 
         // Clear entire backstack and start in new root
         mState.onRootChanged(root);
-        mSearchManager.update(root);
 
         // Recents is always in memory, so we just load it directly.
         // Otherwise we delegate loading data from disk to a task
@@ -370,18 +373,18 @@
      * e.g. The current directory name displayed on the action bar won't get updated.
      */
     @Override
-    public void onSearchChanged() {
-        refreshDirectory(ANIM_NONE);
+    public void onSearchChanged(@Nullable String query) {
+        // We should not get here if root is not searchable
+        checkState(canSearchRoot());
+        reloadSearch(query);
     }
 
-    /**
-     * Called when search query changed.
-     * Updates the state object.
-     * @param query - New query
-     */
-    @Override
-    public void onSearchQueryChanged(String query) {
-        mState.currentSearch = query;
+    private void reloadSearch(String query) {
+        FragmentManager fm = getFragmentManager();
+        RootInfo root = getCurrentRoot();
+        DocumentInfo cwd = getCurrentDirectory();
+
+        DirectoryFragment.reloadSearch(fm, root, cwd, query);
     }
 
     final List<String> getExcludedAuthorities() {
@@ -486,7 +489,8 @@
     @Override
     protected void onSaveInstanceState(Bundle state) {
         super.onSaveInstanceState(state);
-        state.putParcelable(EXTRA_STATE, mState);
+        state.putParcelable(Shared.EXTRA_STATE, mState);
+        mSearchManager.onSaveInstanceState(state);
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index b0542b9..13b7b14 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -53,19 +53,22 @@
     private final RootInfo mRoot;
     private final Uri mUri;
     private final int mUserSortOrder;
+    private final boolean mSearchMode;
 
     private DocumentInfo mDoc;
     private CancellationSignal mSignal;
     private DirectoryResult mResult;
 
+
     public DirectoryLoader(Context context, int type, RootInfo root, DocumentInfo doc, Uri uri,
-            int userSortOrder) {
+            int userSortOrder, boolean inSearchMode) {
         super(context, ProviderExecutor.forAuthority(root.authority));
         mType = type;
         mRoot = root;
         mUri = uri;
         mUserSortOrder = userSortOrder;
         mDoc = doc;
+        mSearchMode = inSearchMode;
     }
 
     @Override
@@ -83,7 +86,7 @@
         final DirectoryResult result = new DirectoryResult();
 
         // Use default document when searching
-        if (mType == DirectoryFragment.TYPE_SEARCH) {
+        if (mSearchMode) {
             final Uri docUri = DocumentsContract.buildDocumentUri(
                     mRoot.authority, mRoot.documentId);
             try {
@@ -106,7 +109,7 @@
         }
 
         // Search always uses ranking from provider
-        if (mType == DirectoryFragment.TYPE_SEARCH) {
+        if (mSearchMode) {
             result.sortOrder = State.SORT_ORDER_UNKNOWN;
         }
 
@@ -127,7 +130,7 @@
 
             cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
 
-            if (mType == DirectoryFragment.TYPE_SEARCH) {
+            if (mSearchMode) {
                 // Filter directories out of search results, for now
                 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ec7dde9..9812495 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -290,13 +290,8 @@
                 mState.derivedMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
             }
         } else {
-            if (mSearchManager.isSearching()) {
-                // Ongoing search
-                DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
-            } else {
                 // Normal boring directory
                 DirectoryFragment.showDirectory(fm, root, cwd, anim);
-            }
         }
 
         // Forget any replacement target
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
index f7a45e2..9609dee 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
@@ -16,8 +16,10 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.State.ACTION_MANAGE;
 import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
+import static com.android.internal.util.Preconditions.checkState;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -119,17 +121,14 @@
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
+        if (DEBUG) checkState(!mSearchManager.isSearching());
+
         // If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed
         // root).
         Preconditions.checkNotNull(cwd);
 
-        if (mState.currentSearch != null) {
-            // Ongoing search
-            DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
-        } else {
-            // Normal boring directory
-            DirectoryFragment.showDirectory(fm, root, cwd, anim);
-        }
+        // Normal boring directory
+        DirectoryFragment.showDirectory(fm, root, cwd, anim);
     }
 
     @Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c0faba3..b8b50e4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -82,7 +82,6 @@
 
         if (mState.restored) {
             if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
-            refreshCurrentRootAndDirectory(ANIM_NONE);
         } else if (!mState.stack.isEmpty()) {
             // If a non-empty stack is present in our state it was read (presumably)
             // from EXTRA_STACK intent extra. In this case, we'll skip other means of
@@ -248,16 +247,13 @@
         final RootInfo root = getCurrentRoot();
         final DocumentInfo cwd = getCurrentDirectory();
 
+        if (DEBUG) checkState(!mSearchManager.isSearching());
+
         if (cwd == null) {
             DirectoryFragment.showRecentsOpen(fm, anim);
         } else {
-            if (mState.currentSearch != null) {
-                // Ongoing search
-                DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
-            } else {
-                // Normal boring directory
-                DirectoryFragment.showDirectory(fm, root, cwd, anim);
-            }
+            // Normal boring directory
+            DirectoryFragment.showDirectory(fm, root, cwd, anim);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
index 025faea..27d6797 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -22,6 +22,7 @@
 import static com.android.documentsui.Shared.DEBUG;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
@@ -33,6 +34,7 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
+import android.content.UriPermission;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
@@ -62,6 +64,8 @@
     private static final String EXTRA_APP_LABEL = "com.android.documentsui.APP_LABEL";
     private static final String EXTRA_VOLUME_LABEL = "com.android.documentsui.VOLUME_LABEL";
 
+    private ContentProviderClient mExternalStorageClient;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -98,12 +102,20 @@
         }
     }
 
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mExternalStorageClient != null) {
+            mExternalStorageClient.close();
+        }
+    }
+
     /**
      * Validates the given path (volume + directory) and display the appropriate dialog asking the
      * user to grant access to it.
      */
-    private static boolean showFragment(Activity activity, int userId, StorageVolume storageVolume,
-            String directoryName) {
+    private static boolean showFragment(OpenExternalDirectoryActivity activity, int userId,
+            StorageVolume storageVolume, String directoryName) {
         if (DEBUG)
             Log.d(TAG, "showFragment() for volume " + storageVolume.dump() + ", directory "
                     + directoryName + ", and user " + userId);
@@ -129,7 +141,7 @@
             return false;
         }
 
-        // Gets volume label and converted path
+        // Gets volume label and converted path.
         String volumeLabel = null;
         final List<VolumeInfo> volumes = sm.getVolumes();
         if (DEBUG) Log.d(TAG, "Number of volumes: " + volumes.size());
@@ -143,6 +155,15 @@
                 break;
             }
         }
+
+        // Checks if the user has granted the permission already.
+        final Intent intent = getIntentForExistingPermission(activity, file);
+        if (intent != null) {
+            activity.setResult(RESULT_OK, intent);
+            activity.finish();
+            return true;
+        }
+
         if (volumeLabel == null) {
             Log.e(TAG, "Could not get volume for " + file);
             return false;
@@ -196,8 +217,7 @@
         return volume.isVisibleForWrite(userId) && root.equals(path);
     }
 
-    private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider,
-            File file) {
+    private static Uri getGrantedUriPermission(ContentProviderClient provider, File file) {
         // Calls ExternalStorageProvider to get the doc id for the file
         final Bundle bundle;
         try {
@@ -218,8 +238,17 @@
             Log.e(TAG, "Could not get URI for doc id " + docId);
             return null;
         }
-
         if (DEBUG) Log.d(TAG, "URI for " + file + ": " + uri);
+        return uri;
+    }
+
+    private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider,
+            File file) {
+        final Uri uri = getGrantedUriPermission(provider, file);
+        return createGrantedUriPermissionsIntent(uri);
+    }
+
+    private static Intent createGrantedUriPermissionsIntent(Uri uri) {
         final Intent intent = new Intent();
         intent.setData(uri);
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -229,13 +258,31 @@
         return intent;
     }
 
+    private static Intent getIntentForExistingPermission(OpenExternalDirectoryActivity activity,
+            File file) {
+        final String packageName = activity.getCallingPackage();
+        final Uri grantedUri = getGrantedUriPermission(activity.getExternalStorageClient(), file);
+        if (DEBUG)
+            Log.d(TAG, "checking if " + packageName + " already has permission for " + grantedUri);
+        final ActivityManager am =
+                (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
+        for (UriPermission uriPermission : am.getGrantedUriPermissions(packageName).getList()) {
+            final Uri uri = uriPermission.getUri();
+            if (uri.equals(grantedUri)) {
+                if (DEBUG) Log.d(TAG, packageName + " already has permission: " + uriPermission);
+                return createGrantedUriPermissionsIntent(uri);
+            }
+        }
+        if (DEBUG) Log.d(TAG, packageName + " does not have permission for " + grantedUri);
+        return null;
+    }
+
     public static class OpenExternalDirectoryDialogFragment extends DialogFragment {
 
         private File mFile;
         private String mVolumeLabel;
         private String mAppLabel;
-        private ContentProviderClient mExternalStorageClient;
-        private ContentResolver mResolver;
+        private OpenExternalDirectoryActivity mActivity;
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -245,16 +292,8 @@
                 mFile = new File(args.getString(EXTRA_FILE));
                 mVolumeLabel = args.getString(EXTRA_VOLUME_LABEL);
                 mAppLabel = args.getString(EXTRA_APP_LABEL);
-                mResolver = getContext().getContentResolver();
             }
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            if (mExternalStorageClient != null) {
-                mExternalStorageClient.close();
-            }
+            mActivity = (OpenExternalDirectoryActivity) getActivity();
         }
 
         @Override
@@ -267,8 +306,8 @@
                 public void onClick(DialogInterface dialog, int which) {
                     Intent intent = null;
                     if (which == DialogInterface.BUTTON_POSITIVE) {
-                        intent = createGrantedUriPermissionsIntent(getExternalStorageClient(),
-                                mFile);
+                        intent = createGrantedUriPermissionsIntent(
+                                mActivity.getExternalStorageClient(), mFile);
                     }
                     if (which == DialogInterface.BUTTON_NEGATIVE || intent == null) {
                         activity.setResult(RESULT_CANCELED);
@@ -297,13 +336,13 @@
             activity.setResult(RESULT_CANCELED);
             activity.finish();
         }
+    }
 
-        private synchronized ContentProviderClient getExternalStorageClient() {
-            if (mExternalStorageClient == null) {
-                mExternalStorageClient =
-                        mResolver.acquireContentProviderClient(EXTERNAL_STORAGE_AUTH);
-            }
-            return mExternalStorageClient;
+    private synchronized ContentProviderClient getExternalStorageClient() {
+        if (mExternalStorageClient == null) {
+            mExternalStorageClient =
+                    getContentResolver().acquireContentProviderClient(EXTERNAL_STORAGE_AUTH);
         }
+        return mExternalStorageClient;
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
similarity index 78%
rename from packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
rename to packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 69f54c7..0496862 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -16,6 +16,8 @@
 
 package com.android.documentsui;
 
+import android.annotation.Nullable;
+import android.os.Bundle;
 import android.provider.DocumentsContract.Root;
 import android.text.TextUtils;
 import android.util.Log;
@@ -31,28 +33,27 @@
 /**
  * Manages searching UI behavior.
  */
-final class SearchManager implements
+final class SearchViewManager implements
         SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener {
 
     public interface SearchManagerListener {
-        void onSearchChanged();
-
-        void onSearchQueryChanged(String query);
+        void onSearchChanged(@Nullable String query);
     }
 
     public static final String TAG = "SearchManger";
 
     private SearchManagerListener mListener;
-    private String currentSearch;
     private boolean mSearchExpanded;
+    private String mCurrentSearch;
     private boolean mIgnoreNextClose;
 
     private DocumentsToolbar mActionBar;
     private MenuItem mMenu;
     private SearchView mView;
 
-    public SearchManager(SearchManagerListener listener) {
+    public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) {
         mListener = listener;
+        mCurrentSearch = savedState != null ? savedState.getString(Shared.EXTRA_QUERY) : null;
     }
 
     public void setSearchMangerListener(SearchManagerListener listener) {
@@ -69,6 +70,8 @@
         mView.setOnCloseListener(this);
         mView.setOnSearchClickListener(this);
         mView.setOnQueryTextFocusChangeListener(this);
+
+        restoreSearch();
     }
 
     /**
@@ -80,12 +83,12 @@
             return;
         }
 
-        if (currentSearch != null) {
+        if (mCurrentSearch != null) {
             mMenu.expandActionView();
 
             mView.setIconified(false);
             mView.clearFocus();
-            mView.setQuery(currentSearch, false);
+            mView.setQuery(mCurrentSearch, false);
         } else {
             mView.clearFocus();
             if (!mView.isIconified()) {
@@ -108,13 +111,11 @@
             return;
         }
 
-        mMenu.setVisible(visible);
         if (!visible) {
-            currentSearch = null;
-            if (mListener != null) {
-                mListener.onSearchQueryChanged(currentSearch);
-            }
+            mCurrentSearch = null;
         }
+
+        mMenu.setVisible(visible);
     }
 
     /**
@@ -133,8 +134,23 @@
         return false;
     }
 
+    private void restoreSearch() {
+        if (isSearching()) {
+            onSearchExpanded();
+            mView.setIconified(false);
+            mView.setQuery(mCurrentSearch, false);
+            mView.clearFocus();
+        }
+    }
+
+    private void onSearchExpanded() {
+        mSearchExpanded = true;
+        mView.setBackgroundColor(
+                mView.getResources().getColor(R.color.menu_search_background, null));
+    }
+
     boolean isSearching() {
-        return currentSearch != null;
+        return mCurrentSearch != null;
     }
 
     boolean isExpanded() {
@@ -142,6 +158,14 @@
     }
 
     /**
+     * Called when owning activity is saving state to be used to restore state during creation.
+     * @param state Bundle to save state too
+     */
+    public void onSaveInstanceState(Bundle state) {
+        state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
+    }
+
+    /**
      * Clears the search. Clears the SearchView background color. Triggers refreshing of the
      * directory content.
      * @return True if the default behavior of clearing/dismissing SearchView should be overridden.
@@ -159,11 +183,10 @@
                 mView.getResources().getColor(android.R.color.transparent, null));
 
         // Refresh the directory if a search was done
-        if (currentSearch != null) {
-            currentSearch = null;
+        if (mCurrentSearch != null) {
+            mCurrentSearch = null;
             if (mListener != null) {
-                mListener.onSearchQueryChanged(currentSearch);
-                mListener.onSearchChanged();
+                mListener.onSearchChanged(mCurrentSearch);
             }
         }
         return false;
@@ -176,18 +199,15 @@
      */
     @Override
     public void onClick(View v) {
-        mSearchExpanded = true;
-        mView.setBackgroundColor(
-                mView.getResources().getColor(R.color.menu_search_background, null));
+        onSearchExpanded();
     }
 
     @Override
     public boolean onQueryTextSubmit(String query) {
-        currentSearch = query;
+        mCurrentSearch = query;
         mView.clearFocus();
         if (mListener != null) {
-            mListener.onSearchQueryChanged(currentSearch);
-            mListener.onSearchChanged();
+            mListener.onSearchChanged(mCurrentSearch);
         }
         return true;
     }
@@ -195,7 +215,7 @@
     @Override
     public void onFocusChange(View v, boolean hasFocus) {
         if (!hasFocus) {
-            if (currentSearch == null) {
+            if (mCurrentSearch == null) {
                 mView.setIconified(true);
             } else if (TextUtils.isEmpty(mView.getQuery())) {
                 cancelSearch();
@@ -207,4 +227,9 @@
     public boolean onQueryTextChange(String newText) {
         return false;
     }
+
+    String getCurrentSearch() {
+        return mCurrentSearch;
+    }
+
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index b90a119..a288fe8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -37,10 +37,50 @@
      * specifies if the destination directory needs to create new directory or not.
      */
     public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
+    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
+
+    /**
+     * Extra flag used to store query of type String in the bundle.
+     */
+    public static final String EXTRA_QUERY = "query";
+
+    /**
+     * Extra flag used to store state of type State in the bundle.
+     */
+    public static final String EXTRA_STATE = "state";
+
+    /**
+     * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
+     */
+    public static final String EXTRA_TYPE = "type";
+
+    /**
+     * Extra flag used to store root of type RootInfo in the bundle.
+     */
+    public static final String EXTRA_ROOT = "root";
+
+    /**
+     * Extra flag used to store document of DocumentInfo type in the bundle.
+     */
+    public static final String EXTRA_DOC = "document";
+
+    /**
+     * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
+     */
+    public static final String EXTRA_SELECTION = "selection";
+
+    /**
+     * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
+     */
+    public static final String EXTRA_SEARCH_MODE = "searchMode";
+
+    /**
+     * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
+     */
+    public static final String EXTRA_IGNORE_STATE = "ignoreState";
 
     public static final boolean DEBUG = true;
     public static final String TAG = "Documents";
-    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
 
 
     /**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 0948ab1..2ecbdf6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -105,9 +105,6 @@
     private boolean mInitialRootChanged;
     private boolean mInitialDocChanged;
 
-    /** Currently active search, overriding any stack. */
-    public String currentSearch;
-
     /** Instance state for every shown directory */
     public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
 
@@ -186,7 +183,6 @@
         out.writeInt(showAdvanced ? 1 : 0);
         out.writeInt(restored ? 1 : 0);
         DurableUtils.writeToParcel(out, stack);
-        out.writeString(currentSearch);
         out.writeMap(dirState);
         out.writeParcelable(selectedDocuments, 0);
         out.writeList(selectedDocumentsForCopy);
@@ -217,7 +213,6 @@
             state.showAdvanced = in.readInt() != 0;
             state.restored = in.readInt() != 0;
             DurableUtils.readFromParcel(in, state.stack);
-            state.currentSearch = in.readString();
             in.readMap(state.dirState, loader);
             state.selectedDocuments = in.readParcelable(loader);
             in.readList(state.selectedDocumentsForCopy, loader);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 4583dec..669eb71 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -113,19 +113,26 @@
 /**
  * Display the documents inside a single directory.
  */
-public class DirectoryFragment extends Fragment implements DocumentsAdapter.Environment {
+public class DirectoryFragment extends Fragment
+        implements DocumentsAdapter.Environment, LoaderCallbacks<DirectoryResult> {
 
     @IntDef(flag = true, value = {
             TYPE_NORMAL,
-            TYPE_SEARCH,
             TYPE_RECENT_OPEN
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResultType {}
     public static final int TYPE_NORMAL = 1;
-    public static final int TYPE_SEARCH = 2;
-    public static final int TYPE_RECENT_OPEN = 3;
+    public static final int TYPE_RECENT_OPEN = 2;
 
+    @IntDef(flag = true, value = {
+            ANIM_NONE,
+            ANIM_SIDE,
+            ANIM_LEAVE,
+            ANIM_ENTER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AnimationType {}
     public static final int ANIM_NONE = 1;
     public static final int ANIM_SIDE = 2;
     public static final int ANIM_LEAVE = 3;
@@ -146,12 +153,6 @@
     private static final int DELETE_JOB_DELAY = 5500;
     private static final int EMPTY_REVEAL_DURATION = 250;
 
-    private static final String EXTRA_TYPE = "type";
-    private static final String EXTRA_ROOT = "root";
-    private static final String EXTRA_DOC = "doc";
-    private static final String EXTRA_QUERY = "query";
-    private static final String EXTRA_IGNORE_STATE = "ignoreState";
-
     private Model mModel;
     private MultiSelectManager mSelectionManager;
     private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
@@ -164,12 +165,10 @@
     private RecyclerView mRecView;
     private ListeningGestureDetector mGestureDetector;
 
-    private @ResultType int mType = TYPE_NORMAL;
     private String mStateKey;
 
     private int mLastSortOrder = SORT_ORDER_UNKNOWN;
     private DocumentsAdapter mAdapter;
-    private LoaderCallbacks<DirectoryResult> mCallbacks;
     private FragmentTuner mTuner;
     private DocumentClipper mClipper;
     private GridLayoutManager mLayout;
@@ -178,6 +177,14 @@
     private MessageBar mMessageBar;
     private View mProgressBar;
 
+    // Directory fragment state is defined by: root, document, query, type, selection
+    private @ResultType int mType = TYPE_NORMAL;
+    private RootInfo mRoot;
+    private DocumentInfo mDocument;
+    private String mQuery = null;
+    private Selection mSelection = null;
+    private boolean mSearchMode = false;
+
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -226,9 +233,16 @@
         final Context context = getActivity();
         final State state = getDisplayState();
 
-        final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
-        final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
-        mStateKey = buildStateKey(root, doc);
+        // Read arguments when object created for the first time.
+        // Restore state if fragment recreated.
+        Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState;
+        mRoot = args.getParcelable(Shared.EXTRA_ROOT);
+        mDocument = args.getParcelable(Shared.EXTRA_DOC);
+        mStateKey = buildStateKey(mRoot, mDocument);
+        mQuery = args.getString(Shared.EXTRA_QUERY);
+        mType = args.getInt(Shared.EXTRA_TYPE);
+        mSelection = args.getParcelable(Shared.EXTRA_SELECTION);
+        mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE);
 
         mIconHelper = new IconHelper(context, MODE_GRID);
 
@@ -248,13 +262,6 @@
 
         mRecView.addOnItemTouchListener(mGestureDetector);
 
-        // final here because we'll manually bump the listener iwhen we had an initial selection,
-        // but only after the model is fully loaded.
-        final SelectionModeListener selectionListener = new SelectionModeListener();
-        final Selection initialSelection = state.selectedDocuments.hasDirectoryKey(mStateKey)
-            ? state.selectedDocuments
-            : null;
-
         // TODO: instead of inserting the view into the constructor, extract listener-creation code
         // and set the listener on the view after the fact.  Then the view doesn't need to be passed
         // into the selection manager.
@@ -264,9 +271,9 @@
                 state.allowMultiple
                     ? MultiSelectManager.MODE_MULTIPLE
                     : MultiSelectManager.MODE_SINGLE,
-                initialSelection);
+                null);
 
-        mSelectionManager.addCallback(selectionListener);
+        mSelectionManager.addCallback(new SelectionModeListener());
 
         mModel = new Model();
         mModel.addUpdateListener(mAdapter);
@@ -275,8 +282,6 @@
         // Make sure this is done after the RecyclerView is set up.
         mFocusManager = new FocusManager(context, mRecView, mModel);
 
-        mType = getArguments().getInt(EXTRA_TYPE);
-
         mTuner = FragmentTuner.pick(getContext(), state);
         mClipper = new DocumentClipper(context);
 
@@ -286,7 +291,7 @@
             hideGridTitles = MimePredicate.mimeMatches(
                     MimePredicate.VISUAL_MIMES, state.acceptMimes);
         } else {
-            hideGridTitles = (doc != null) && doc.isGridTitlesHidden();
+            hideGridTitles = (mDocument != null) && mDocument.isGridTitlesHidden();
         }
         GridDocumentHolder.setHideTitles(hideGridTitles);
 
@@ -295,86 +300,20 @@
         boolean svelte = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
         mIconHelper.setThumbnailsEnabled(!svelte);
 
-        mCallbacks = new LoaderCallbacks<DirectoryResult>() {
-            @Override
-            public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
-                final String query = getArguments().getString(EXTRA_QUERY);
-
-                Uri contentsUri;
-                switch (mType) {
-                    case TYPE_NORMAL:
-                        contentsUri = DocumentsContract.buildChildDocumentsUri(
-                                doc.authority, doc.documentId);
-                        if (state.action == ACTION_MANAGE) {
-                            contentsUri = DocumentsContract.setManageMode(contentsUri);
-                        }
-                        return new DirectoryLoader(
-                                context, mType, root, doc, contentsUri, state.userSortOrder);
-                    case TYPE_SEARCH:
-                        contentsUri = DocumentsContract.buildSearchDocumentsUri(
-                                root.authority, root.rootId, query);
-                        if (state.action == ACTION_MANAGE) {
-                            contentsUri = DocumentsContract.setManageMode(contentsUri);
-                        }
-                        return new DirectoryLoader(
-                                context, mType, root, doc, contentsUri, state.userSortOrder);
-                    case TYPE_RECENT_OPEN:
-                        final RootsCache roots = DocumentsApplication.getRootsCache(context);
-                        return new RecentsLoader(context, roots, state);
-                    default:
-                        throw new IllegalStateException("Unknown type " + mType);
-                }
-            }
-
-            @Override
-            public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
-                if (!isAdded()) return;
-
-                mModel.update(result);
-                state.derivedSortOrder = result.sortOrder;
-
-                updateDisplayState();
-
-                if (initialSelection != null) {
-                    selectionListener.onSelectionChanged();
-                }
-
-                // Restore any previous instance state
-                final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
-                if (container != null && !getArguments().getBoolean(EXTRA_IGNORE_STATE, false)) {
-                    getView().restoreHierarchyState(container);
-                } else if (mLastSortOrder != state.derivedSortOrder) {
-                    // The derived sort order takes the user sort order into account, but applies
-                    // directory-specific defaults when the user doesn't explicitly set the sort
-                    // order. Scroll to the top if the sort order actually changed.
-                    mRecView.smoothScrollToPosition(0);
-                }
-
-                mLastSortOrder = state.derivedSortOrder;
-
-                mTuner.onModelLoaded(mModel, mType);
-            }
-
-            @Override
-            public void onLoaderReset(Loader<DirectoryResult> loader) {
-                mModel.update(null);
-            }
-        };
-
         // Kick off loader at least once
-        getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+        getLoaderManager().restartLoader(LOADER_ID, null, this);
     }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
-        State state = getDisplayState();
-        if (mSelectionManager.hasSelection()) {
-            mSelectionManager.getSelection(state.selectedDocuments);
-            state.selectedDocuments.setDirectoryKey(mStateKey);
-            if (!state.selectedDocuments.isEmpty()) {
-                if (DEBUG) Log.d(TAG, "Persisted selection: " + state.selectedDocuments);
-            }
-        }
+        super.onSaveInstanceState(outState);
+
+        outState.putInt(Shared.EXTRA_TYPE, mType);
+        outState.putParcelable(Shared.EXTRA_ROOT, mRoot);
+        outState.putParcelable(Shared.EXTRA_DOC, mDocument);
+        outState.putString(Shared.EXTRA_QUERY, mQuery);
+        outState.putParcelable(Shared.EXTRA_SELECTION, mSelectionManager.getSelection());
+        outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
     }
 
     @Override
@@ -449,7 +388,7 @@
     public void onSortOrderChanged() {
         // Sort order is implemented as a sorting wrapper around directory
         // results. So when sort order changes, we force a reload of the directory.
-        getLoaderManager().restartLoader(LOADER_ID, null, mCallbacks);
+        getLoaderManager().restartLoader(LOADER_ID, null, this);
     }
 
     public void onViewModeChanged() {
@@ -1342,7 +1281,7 @@
             mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
 
             if (model.isEmpty()) {
-                if (getDisplayState().currentSearch != null) {
+                if (mSearchMode) {
                     showNoResults(getDisplayState().stack.root);
                 } else {
                     showEmptyDirectory();
@@ -1468,32 +1407,50 @@
 
     public static void showDirectory(
             FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
-        show(fm, TYPE_NORMAL, root, doc, null, anim);
-    }
-
-    public static void showSearch(FragmentManager fm, RootInfo root, String query, int anim) {
-        show(fm, TYPE_SEARCH, root, null, query, anim);
+        create(fm, TYPE_NORMAL, root, doc, null, anim);
     }
 
     public static void showRecentsOpen(FragmentManager fm, int anim) {
-        show(fm, TYPE_RECENT_OPEN, null, null, null, anim);
+        create(fm, TYPE_RECENT_OPEN, null, null, null, anim);
     }
 
-    private static void show(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
+    public static void reloadSearch(FragmentManager fm, RootInfo root, DocumentInfo doc,
+            String query) {
+        DirectoryFragment df = get(fm);
+
+        df.mQuery = query;
+        df.mRoot = root;
+        df.mDocument = doc;
+        df.mSearchMode =  query != null;
+        df.getLoaderManager().restartLoader(LOADER_ID, null, df);
+    }
+
+    public static void reload(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
+            String query) {
+        DirectoryFragment df = get(fm);
+        df.mType = type;
+        df.mQuery = query;
+        df.mRoot = root;
+        df.mDocument = doc;
+        df.mSearchMode =  query != null;
+        df.getLoaderManager().restartLoader(LOADER_ID, null, df);
+    }
+
+    public static void create(FragmentManager fm, int type, RootInfo root, DocumentInfo doc,
             String query, int anim) {
         final Bundle args = new Bundle();
-        args.putInt(EXTRA_TYPE, type);
-        args.putParcelable(EXTRA_ROOT, root);
-        args.putParcelable(EXTRA_DOC, doc);
-        args.putString(EXTRA_QUERY, query);
+        args.putInt(Shared.EXTRA_TYPE, type);
+        args.putParcelable(Shared.EXTRA_ROOT, root);
+        args.putParcelable(Shared.EXTRA_DOC, doc);
+        args.putString(Shared.EXTRA_QUERY, query);
 
         final FragmentTransaction ft = fm.beginTransaction();
         switch (anim) {
             case ANIM_SIDE:
-                args.putBoolean(EXTRA_IGNORE_STATE, true);
+                args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
                 break;
             case ANIM_ENTER:
-                args.putBoolean(EXTRA_IGNORE_STATE, true);
+                args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
                 ft.setCustomAnimations(R.animator.dir_enter, R.animator.dir_frozen);
                 break;
             case ANIM_LEAVE:
@@ -1504,7 +1461,7 @@
         final DirectoryFragment fragment = new DirectoryFragment();
         fragment.setArguments(args);
 
-        ft.replace(R.id.container_directory, fragment);
+        ft.replace(getFragmentId(), fragment);
         ft.commitAllowingStateLoss();
     }
 
@@ -1518,9 +1475,77 @@
 
     public static @Nullable DirectoryFragment get(FragmentManager fm) {
         // TODO: deal with multiple directories shown at once
-        Fragment fragment = fm.findFragmentById(R.id.container_directory);
+        Fragment fragment = fm.findFragmentById(getFragmentId());
         return fragment instanceof DirectoryFragment
                 ? (DirectoryFragment) fragment
                 : null;
     }
-}
+
+    private static int getFragmentId() {
+        return R.id.container_directory;
+    }
+
+    @Override
+    public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
+        Context context = getActivity();
+        State state = getDisplayState();
+
+        Uri contentsUri;
+        switch (mType) {
+            case TYPE_NORMAL:
+                contentsUri = mSearchMode ? DocumentsContract.buildSearchDocumentsUri(
+                        mRoot.authority, mRoot.rootId, mQuery)
+                        : DocumentsContract.buildChildDocumentsUri(
+                                mDocument.authority, mDocument.documentId);
+                if (state.action == ACTION_MANAGE) {
+                    contentsUri = DocumentsContract.setManageMode(contentsUri);
+                }
+                return new DirectoryLoader(
+                        context, mType, mRoot, mDocument, contentsUri, state.userSortOrder, mSearchMode);
+            case TYPE_RECENT_OPEN:
+                final RootsCache roots = DocumentsApplication.getRootsCache(context);
+                return new RecentsLoader(context, roots, state);
+            default:
+                throw new IllegalStateException("Unknown type " + mType);
+        }
+    }
+
+    @Override
+    public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
+        if (!isAdded()) return;
+
+        State state = getDisplayState();
+
+        mAdapter.notifyDataSetChanged();
+        mModel.update(result);
+
+        state.derivedSortOrder = result.sortOrder;
+
+        updateLayout(state.derivedMode);
+
+        if (mSelection != null) {
+            mSelectionManager.setItemsSelected(mSelection.toList(), true);
+        }
+
+        // Restore any previous instance state
+        final SparseArray<Parcelable> container = state.dirState.remove(mStateKey);
+        if (container != null && !getArguments().getBoolean(Shared.EXTRA_IGNORE_STATE, false)) {
+            getView().restoreHierarchyState(container);
+        } else if (mLastSortOrder != state.derivedSortOrder) {
+            // The derived sort order takes the user sort order into account, but applies
+            // directory-specific defaults when the user doesn't explicitly set the sort
+            // order. Scroll to the top if the sort order actually changed.
+            mRecView.smoothScrollToPosition(0);
+        }
+
+        mLastSortOrder = state.derivedSortOrder;
+
+        mTuner.onModelLoaded(mModel, mType, mSearchMode);
+
+    }
+
+    @Override
+    public void onLoaderReset(Loader<DirectoryResult> loader) {
+        mModel.update(null);
+    }
+  }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index a9b0fd1..914f71e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -84,7 +84,7 @@
         return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType);
     }
 
-    abstract void onModelLoaded(Model model, @ResultType int resultType);
+    abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch);
 
     /**
      * Provides support for Platform specific specializations of DirectoryFragment.
@@ -166,7 +166,7 @@
         }
 
         @Override
-        void onModelLoaded(Model model, @ResultType int resultType) {
+        void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
             // When launched into empty recents, show drawer
             if (resultType == DirectoryFragment.TYPE_RECENT_OPEN
                     && model.isEmpty()
@@ -211,7 +211,7 @@
         }
 
         @Override
-        void onModelLoaded(Model model, @ResultType int resultType) {}
+        void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {}
     }
 
     /**
@@ -248,11 +248,10 @@
         }
 
         @Override
-        void onModelLoaded(Model model, @ResultType int resultType) {
+        void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
             if (DEBUG) Log.d(TAG, "Handling model loaded. Has Location shcnage: " + mState.initialLocationHasChanged());
             // When launched into empty root, open drawer.
-            if (model.isEmpty() && !mState.initialLocationHasChanged()
-                    && resultType != DirectoryFragment.TYPE_SEARCH) {
+            if (model.isEmpty() && !mState.initialLocationHasChanged() && !isSearch) {
                 if (DEBUG) Log.d(TAG, "Showing roots drawer cuz stuffs empty.");
 
                 // This noops on layouts without drawer, so no need to guard.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index c8b6f85..19268d7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -687,7 +687,7 @@
          * Returns an unordered array of selected positions (including any
          * provisional selections current in effect).
          */
-        private List<String> toList() {
+        public List<String> toList() {
             ArrayList<String> selection = new ArrayList<String>(mSelection);
             selection.addAll(mProvisionalSelection);
             return selection;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 681b39e..41cce74 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -18,11 +18,14 @@
 
 import android.content.Context;
 import android.util.Log;
+import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 
@@ -66,4 +69,9 @@
             ViewGroup container) {
         return new KeyguardBouncer(context, callback, lockPatternUtils, windowManager, container);
     }
+
+    public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
+            View headsUpScrim, boolean scrimSrcEnabled) {
+        return new ScrimController(scrimBehind, scrimInFront, headsUpScrim, scrimSrcEnabled);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 1a0acbe..8381f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -94,7 +94,7 @@
     private TextView mClockView;
     private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
-    private NotificationsQuickSettingsContainer mNotificationContainerParent;
+    protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
     private boolean mAnimateNextTopPaddingChange;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d60ea20..2e1855c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -111,6 +111,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeHost;
@@ -771,8 +772,8 @@
         ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
         View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
-        mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim,
-                mScrimSrcModeEnabled);
+        mScrimController = SystemUIFactory.getInstance().createScrimController(
+                scrimBehind, scrimInFront, headsUpScrim, mScrimSrcModeEnabled);
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mScrimController.setBackDropView(mBackdrop);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f310c2c2..fe76ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -56,16 +56,16 @@
     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
 
-    private final ScrimView mScrimBehind;
+    protected final ScrimView mScrimBehind;
     private final ScrimView mScrimInFront;
     private final UnlockMethodCache mUnlockMethodCache;
     private final View mHeadsUpScrim;
 
-    private boolean mKeyguardShowing;
+    protected boolean mKeyguardShowing;
     private float mFraction;
 
     private boolean mDarkenWhileDragging;
-    private boolean mBouncerShowing;
+    protected boolean mBouncerShowing;
     private boolean mWakeAndUnlocking;
     private boolean mAnimateChange;
     private boolean mUpdatePending;
@@ -203,7 +203,7 @@
         mUpdatePending = true;
     }
 
-    private void updateScrims() {
+    protected void updateScrims() {
         if (mAnimateKeyguardFadingOut || mForceHideScrims) {
             setScrimInFrontColor(0f);
             setScrimBehindColor(0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index f078b53..8f9a224 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3045,7 +3045,7 @@
             disableClipOptimization();
         }
         handleDismissAllClipping();
-        if (mCurrIconRow != null & mCurrIconRow.isVisible()) {
+        if (mCurrIconRow != null && mCurrIconRow.isVisible()) {
             mCurrIconRow.getNotificationParent().animateTranslateNotification(0 /* left target */);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index fac6338..6b943e0 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -101,7 +101,26 @@
         @Override
         public void run() {
             if (mState != STATE_NO_PIP) {
-                // TODO: check whether PIP task is closed.
+                StackInfo stackInfo = null;
+                try {
+                    stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+                    if (stackInfo == null) {
+                        Log.w(TAG, "There is no pinned stack");
+                        closeInternal(false);
+                        return;
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "getStackInfo failed", e);
+                    return;
+                }
+                for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
+                    if (stackInfo.taskIds[i] == mPipTaskId) {
+                        // PIP task is still alive.
+                        return;
+                    }
+                }
+                // PIP task doesn't exist anymore in PINNED_STACK.
+                closeInternal(true);
             }
         }
     };
@@ -203,12 +222,18 @@
      * Closes PIP (PIPed activity and PIP system UI).
      */
     public void closePip() {
+        closeInternal(true);
+    }
+
+    private void closeInternal(boolean removePipStack) {
         mState = STATE_NO_PIP;
         mPipTaskId = TASK_ID_NO_PIP;
-        try {
-            mActivityManager.removeStack(PINNED_STACK_ID);
-        } catch (RemoteException e) {
-            Log.e(TAG, "removeStack failed", e);
+        if (removePipStack) {
+            try {
+                mActivityManager.removeStack(PINNED_STACK_ID);
+            } catch (RemoteException e) {
+                Log.e(TAG, "removeStack failed", e);
+            }
         }
     }
 
diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
deleted file mode 100644
index 51b42a6..0000000
--- a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_RunQueue_Delegate.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate used to provide new implementation of a select few methods of
- * {@link ViewRootImpl.RunQueue}
- *
- * Through the layoutlib_create tool, the original  methods of ViewRootImpl.RunQueue have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- */
-public class ViewRootImpl_RunQueue_Delegate {
-
-    @LayoutlibDelegate
-    /*package*/ static void postDelayed(ViewRootImpl.RunQueue thisQueue, Runnable action, long
-            delayMillis) {
-        // The actual RunQueue is never run and therefore never cleared. This method avoids
-        // runnables to be added to the RunQueue so they do not leak resources.
-    }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 15c567f..8a23e4b 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -184,7 +184,6 @@
         "android.view.View#getWindowToken",
         "android.view.View#isInEditMode",
         "android.view.ViewRootImpl#isInTouchMode",
-        "android.view.ViewRootImpl$RunQueue#postDelayed",
         "android.view.WindowManagerGlobal#getWindowManagerService",
         "android.view.inputmethod.InputMethodManager#getInstance",
         "android.view.MenuInflater#registerMenu",