Remove RecentsCreateFragment, rename RecentsProvider.

RecentsProvider was poorly named, given that it has nothing to do with
"Recents" root. I renamed it so, although a bit wordy, at least decouple
any assumptions of its relationship with Recents root.

Also stripped out RecentsCreateFragment, and now make
CREATE/OPEN_TREE/PICK_COPY_DESTINATION open to default root if last
accessed directory does not exist.

Finally, reverted ag/875528 because I don't think that applies anymore.

Bug: 27307743
Change-Id: I83ed9cc45308f881a58f02beb95922f9b79b3658
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 25b609b..206f9fc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -96,8 +96,8 @@
         </activity>
 
         <provider
-            android:name=".RecentsProvider"
-            android:authorities="com.android.documentsui.recents"
+            android:name=".LastAccessedProvider"
+            android:authorities="com.android.documentsui.lastAccessed"
             android:exported="false"/>
 
         <receiver android:name=".PackageReceiver">
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index 8f9c787..555fbde 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -33,7 +33,6 @@
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -43,25 +42,16 @@
 import android.view.Menu;
 
 import com.android.documentsui.MenuManager.DirectoryDetails;
-import com.android.documentsui.RecentsProvider.RecentColumns;
-import com.android.documentsui.RecentsProvider.ResumeColumns;
-import com.android.documentsui.dirlist.AnimationView;
+import com.android.documentsui.LastAccessedProvider.Columns;
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.dirlist.FragmentTuner;
 import com.android.documentsui.dirlist.FragmentTuner.DocumentsTuner;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
 import com.android.documentsui.services.FileOperationService;
-import com.android.documentsui.sorting.SortController;
 
-import libcore.io.IoUtils;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 
 public class DocumentsActivity extends BaseActivity {
@@ -118,7 +108,7 @@
                 loadRoot(getDefaultRoot());
             } else {
                 if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
-                new LoadLastUsedStackTask(this).execute();
+                new LoadLastAccessedStackTask(this).execute();
             }
         }
     }
@@ -180,8 +170,8 @@
             // 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);
+            values.put(Columns.EXTERNAL, 1);
+            getContentResolver().insert(LastAccessedProvider.buildLastAccessed(packageName), values);
 
             // Pass back result to original caller
             setResult(resultCode, data);
@@ -242,9 +232,8 @@
         if (cwd == null) {
             // No directory means recents
             if (mState.action == ACTION_CREATE ||
-                mState.action == ACTION_OPEN_TREE ||
                 mState.action == ACTION_PICK_COPY_DESTINATION) {
-                RecentsCreateFragment.show(fm);
+                loadRoot(getDefaultRoot());
             } else {
                 DirectoryFragment.showRecentsOpen(fm, anim);
 
@@ -336,27 +325,9 @@
         new PickFinishTask(this, result).executeOnExecutor(getExecutorForCurrentDirectory());
     }
 
-    void writeStackToRecentsBlocking() {
-        final ContentResolver resolver = getContentResolver();
-        final ContentValues values = new ContentValues();
-
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
-        if (mState.action == ACTION_CREATE ||
-            mState.action == ACTION_OPEN_TREE ||
-            mState.action == ACTION_PICK_COPY_DESTINATION) {
-            // Remember stack for last create
-            values.clear();
-            values.put(RecentColumns.KEY, mState.stack.buildKey());
-            values.put(RecentColumns.STACK, rawStack);
-            resolver.insert(RecentsProvider.buildRecent(), values);
-        }
-
-        // Remember location for next app launch
-        final String packageName = getCallingPackageMaybeExtra();
-        values.clear();
-        values.put(ResumeColumns.STACK, rawStack);
-        values.put(ResumeColumns.EXTERNAL, 0);
-        resolver.insert(RecentsProvider.buildResume(packageName), values);
+    void updateLastAccessed() {
+        LastAccessedProvider.setLastAccessed(
+                getContentResolver(), getCallingPackageMaybeExtra(), mState.stack);
     }
 
     @Override
@@ -419,73 +390,6 @@
         return mDetails;
     }
 
-    /**
-     * Loads the last used path (stack) from Recents (history).
-     * The path selected is based on the calling package name. So the last
-     * path for an app like Gmail can be different than the last path
-     * for an app like DropBox.
-     */
-    private static final class LoadLastUsedStackTask
-            extends PairedTask<DocumentsActivity, Void, Void> {
-
-        private volatile boolean mRestoredStack;
-        private volatile boolean mExternal;
-        private State mState;
-
-        public LoadLastUsedStackTask(DocumentsActivity activity) {
-            super(activity);
-            mState = activity.mState;
-        }
-
-        @Override
-        protected Void run(Void... params) {
-            if (DEBUG && !mState.stack.isEmpty()) {
-                Log.w(TAG, "Overwriting existing stack.");
-            }
-            RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
-
-            String packageName = mOwner.getCallingPackageMaybeExtra();
-            Uri resumeUri = RecentsProvider.buildResume(packageName);
-            Cursor cursor = mOwner.getContentResolver().query(resumeUri, 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 = roots.getMatchingRootsBlocking(mState);
-                try {
-                    mState.stack.updateRoot(matchingRoots);
-                    mState.stack.updateDocuments(mOwner.getContentResolver());
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "Failed to restore stack for package: " + packageName
-                            + " because of error: "+ e);
-                    mState.stack.reset();
-                    mRestoredStack = false;
-                }
-            }
-
-            return null;
-        }
-
-        @Override
-        protected void finish(Void result) {
-            mState.restored = true;
-            mState.external = mExternal;
-            mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
-        }
-    }
-
     private static final class PickFinishTask extends PairedTask<DocumentsActivity, Void, Void> {
         private final Uri mUri;
 
@@ -496,7 +400,7 @@
 
         @Override
         protected Void run(Void... params) {
-            mOwner.writeStackToRecentsBlocking();
+            mOwner.updateLastAccessed();
             return null;
         }
 
@@ -516,7 +420,7 @@
 
         @Override
         protected Void run(Void... params) {
-            mOwner.writeStackToRecentsBlocking();
+            mOwner.updateLastAccessed();
             return null;
         }
 
@@ -563,7 +467,7 @@
             }
 
             if (childUri != null) {
-                mOwner.writeStackToRecentsBlocking();
+                mOwner.updateLastAccessed();
             }
 
             return childUri;
diff --git a/src/com/android/documentsui/FilesActivity.java b/src/com/android/documentsui/FilesActivity.java
index 46a663c..c059db1 100644
--- a/src/com/android/documentsui/FilesActivity.java
+++ b/src/com/android/documentsui/FilesActivity.java
@@ -23,8 +23,6 @@
 import android.app.FragmentManager;
 import android.content.ActivityNotFoundException;
 import android.content.ClipData;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -38,7 +36,6 @@
 
 import com.android.documentsui.MenuManager.DirectoryDetails;
 import com.android.documentsui.OperationDialogFragment.DialogType;
-import com.android.documentsui.RecentsProvider.ResumeColumns;
 import com.android.documentsui.clipping.DocumentClipper;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DirectoryFragment;
@@ -47,7 +44,6 @@
 import com.android.documentsui.dirlist.Model;
 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 com.android.documentsui.services.FileOperationService;
 
@@ -439,24 +435,6 @@
         return false;
     }
 
-    // Turns out only DocumentsActivity was ever calling saveStackBlocking.
-    // There may be a  case where we want to contribute entries from
-    // Behavior here in FilesActivity, but it isn't yet obvious.
-    // TODO: Contribute to recents, or remove this.
-    void writeStackToRecentsBlocking() {
-        final ContentResolver resolver = getContentResolver();
-        final ContentValues values = new ContentValues();
-
-        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
-
-        // Remember location for next app launch
-        final String packageName = getCallingPackageMaybeExtra();
-        values.clear();
-        values.put(ResumeColumns.STACK, rawStack);
-        values.put(ResumeColumns.EXTERNAL, 0);
-        resolver.insert(RecentsProvider.buildResume(packageName), values);
-    }
-
     @Override
     void onTaskFinished(Uri... uris) {
         if (DEBUG) Log.d(TAG, "onFinished() " + Arrays.toString(uris));
diff --git a/src/com/android/documentsui/LastAccessedProvider.java b/src/com/android/documentsui/LastAccessedProvider.java
new file mode 100644
index 0000000..ba818cb
--- /dev/null
+++ b/src/com/android/documentsui/LastAccessedProvider.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2013 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 com.android.documentsui;
+
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.util.Log;
+
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.internal.util.Predicate;
+
+import com.google.android.collect.Sets;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.util.Set;
+
+/*
+ * Provider used to keep track of the last known directory navigation trail done by the user
+ */
+public class LastAccessedProvider extends ContentProvider {
+    private static final String TAG = "LastAccessedProvider";
+
+
+    private static final String AUTHORITY = "com.android.documentsui.lastAccessed";
+
+    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    private static final int URI_LAST_ACCESSED = 1;
+
+    public static final String METHOD_PURGE = "purge";
+    public static final String METHOD_PURGE_PACKAGE = "purgePackage";
+
+    static {
+        sMatcher.addURI(AUTHORITY, "lastAccessed/*", URI_LAST_ACCESSED);
+    }
+
+    public static final String TABLE_LAST_ACCESSED = "lastAccessed";
+
+    public static class Columns {
+        public static final String PACKAGE_NAME = "package_name";
+        public static final String STACK = "stack";
+        public static final String TIMESTAMP = "timestamp";
+        // Indicates handler was an external app, like photos.
+        public static final String EXTERNAL = "external";
+    }
+
+    public static Uri buildLastAccessed(String packageName) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(AUTHORITY).appendPath("lastAccessed").appendPath(packageName).build();
+    }
+
+    private DatabaseHelper mHelper;
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String DB_NAME = "lastAccess.db";
+
+        // Used for backwards compatibility
+        private static final int VERSION_INIT = 1;
+        private static final int VERSION_AS_BLOB = 3;
+        private static final int VERSION_ADD_EXTERNAL = 4;
+        private static final int VERSION_ADD_RECENT_KEY = 5;
+
+        private static final int VERSION_LAST_ACCESS_REFACTOR = 6;
+
+        public DatabaseHelper(Context context) {
+            super(context, DB_NAME, null, VERSION_LAST_ACCESS_REFACTOR);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+
+            db.execSQL("CREATE TABLE " + TABLE_LAST_ACCESSED + " (" +
+                    Columns.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY," +
+                    Columns.STACK + " BLOB DEFAULT NULL," +
+                    Columns.TIMESTAMP + " INTEGER," +
+                    Columns.EXTERNAL + " INTEGER NOT NULL DEFAULT 0" +
+                    ")");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.w(TAG, "Upgrading database; wiping app data");
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_ACCESSED);
+            onCreate(db);
+        }
+    }
+
+    public static void setLastAccessed(
+            ContentResolver resolver, String packageName, DocumentStack stack) {
+        final ContentValues values = new ContentValues();
+        final byte[] rawStack = DurableUtils.writeToArrayOrNull(stack);
+        values.clear();
+        values.put(Columns.STACK, rawStack);
+        values.put(Columns.EXTERNAL, 0);
+        resolver.insert(buildLastAccessed(packageName), values);
+    }
+
+    @Override
+    public boolean onCreate() {
+        mHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        if (sMatcher.match(uri) != URI_LAST_ACCESSED) {
+            throw new UnsupportedOperationException("Unsupported Uri " + uri);
+        }
+
+        final SQLiteDatabase db = mHelper.getReadableDatabase();
+        final String packageName = uri.getPathSegments().get(1);
+        return db.query(TABLE_LAST_ACCESSED, projection, Columns.PACKAGE_NAME + "=?",
+                        new String[] { packageName }, null, null, sortOrder);
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        if (sMatcher.match(uri) != URI_LAST_ACCESSED) {
+            throw new UnsupportedOperationException("Unsupported Uri " + uri);
+        }
+
+        final SQLiteDatabase db = mHelper.getWritableDatabase();
+        final ContentValues key = new ContentValues();
+
+        values.put(Columns.TIMESTAMP, System.currentTimeMillis());
+
+        final String packageName = uri.getPathSegments().get(1);
+        key.put(Columns.PACKAGE_NAME, packageName);
+
+        // Ensure that row exists, then update with changed values
+        db.insertWithOnConflict(TABLE_LAST_ACCESSED, null, key, SQLiteDatabase.CONFLICT_IGNORE);
+        db.update(TABLE_LAST_ACCESSED, values, Columns.PACKAGE_NAME + "=?",
+                        new String[] { packageName });
+        return uri;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Unsupported Uri " + uri);
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Unsupported Uri " + uri);
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (METHOD_PURGE.equals(method)) {
+            // Purge references to unknown authorities
+            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+            final Set<String> knownAuth = Sets.newHashSet();
+            for (ResolveInfo info : getContext()
+                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
+                knownAuth.add(info.providerInfo.authority);
+            }
+
+            purgeByAuthority(new Predicate<String>() {
+                @Override
+                public boolean apply(String authority) {
+                    // Purge unknown authorities
+                    return !knownAuth.contains(authority);
+                }
+            });
+
+            return null;
+
+        } else if (METHOD_PURGE_PACKAGE.equals(method)) {
+            // Purge references to authorities in given package
+            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+            intent.setPackage(arg);
+            final Set<String> packageAuth = Sets.newHashSet();
+            for (ResolveInfo info : getContext()
+                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
+                packageAuth.add(info.providerInfo.authority);
+            }
+
+            if (!packageAuth.isEmpty()) {
+                purgeByAuthority(new Predicate<String>() {
+                    @Override
+                    public boolean apply(String authority) {
+                        // Purge authority matches
+                        return packageAuth.contains(authority);
+                    }
+                });
+            }
+
+            return null;
+
+        } else {
+            return super.call(method, arg, extras);
+        }
+    }
+
+    /**
+     * Purge all internal data whose authority matches the given
+     * {@link Predicate}.
+     */
+    private void purgeByAuthority(Predicate<String> predicate) {
+        final SQLiteDatabase db = mHelper.getWritableDatabase();
+        final DocumentStack stack = new DocumentStack();
+
+        Cursor cursor = db.query(TABLE_LAST_ACCESSED, null, null, null, null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                try {
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(Columns.STACK));
+                    DurableUtils.readFromArray(rawStack, stack);
+
+                    if (stack.root != null && predicate.apply(stack.root.authority)) {
+                        final String packageName = getCursorString(
+                                cursor, Columns.PACKAGE_NAME);
+                        db.delete(TABLE_LAST_ACCESSED, Columns.PACKAGE_NAME + "=?",
+                                new String[] { packageName });
+                    }
+                } catch (IOException ignored) {
+                }
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+    }
+}
diff --git a/src/com/android/documentsui/LoadLastAccessedStackTask.java b/src/com/android/documentsui/LoadLastAccessedStackTask.java
new file mode 100644
index 0000000..78e79c7
--- /dev/null
+++ b/src/com/android/documentsui/LoadLastAccessedStackTask.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.documentsui;
+
+import static com.android.documentsui.Shared.DEBUG;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.documentsui.LastAccessedProvider.Columns;
+import com.android.documentsui.dirlist.AnimationView;
+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.Collection;
+
+/**
+ * Loads the last used path (stack) from Recents (history).
+ * The path selected is based on the calling package name. So the last
+ * path for an app like Gmail can be different than the last path
+ * for an app like DropBox.
+ */
+final class LoadLastAccessedStackTask
+        extends PairedTask<DocumentsActivity, Void, Void> {
+
+    private static final String TAG = "LoadLastAccessedStackTask";
+    private volatile boolean mRestoredStack;
+    private volatile boolean mExternal;
+    private State mState;
+
+    public LoadLastAccessedStackTask(DocumentsActivity activity) {
+        super(activity);
+        mState = activity.mState;
+    }
+
+    @Override
+    protected Void run(Void... params) {
+        if (DEBUG && !mState.stack.isEmpty()) {
+            Log.w(TAG, "Overwriting existing stack.");
+        }
+        RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
+
+        String packageName = mOwner.getCallingPackageMaybeExtra();
+        Uri resumeUri = LastAccessedProvider.buildLastAccessed(packageName);
+        Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null);
+        try {
+            if (cursor.moveToFirst()) {
+                mExternal = cursor.getInt(cursor.getColumnIndex(Columns.EXTERNAL)) != 0;
+                final byte[] rawStack = cursor.getBlob(
+                        cursor.getColumnIndex(Columns.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 = roots.getMatchingRootsBlocking(mState);
+            try {
+                mState.stack.updateRoot(matchingRoots);
+                mState.stack.updateDocuments(mOwner.getContentResolver());
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Failed to restore stack for package: " + packageName
+                        + " because of error: "+ e);
+                mState.stack.reset();
+                mRestoredStack = false;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    protected void finish(Void result) {
+        mState.restored = mRestoredStack;
+        mState.external = mExternal;
+        mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/documentsui/PackageReceiver.java b/src/com/android/documentsui/PackageReceiver.java
index b5c7c87..8789171 100644
--- a/src/com/android/documentsui/PackageReceiver.java
+++ b/src/com/android/documentsui/PackageReceiver.java
@@ -23,7 +23,7 @@
 import android.net.Uri;
 
 /**
- * Clean up {@link RecentsProvider} and {@link LocalPreferences} when packages are removed.
+ * Clean up {@link LastAccessedProvider} and {@link LocalPreferences} when packages are removed.
  */
 public class PackageReceiver extends BroadcastReceiver {
     @Override
@@ -35,13 +35,19 @@
         final String packageName = data == null ? null : data.getSchemeSpecificPart();
 
         if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
-            resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE, null, null);
+            resolver.call(
+                    LastAccessedProvider.buildLastAccessed(packageName),
+                    LastAccessedProvider.METHOD_PURGE,
+                    null,
+                    null);
             if (packageName != null) {
                 LocalPreferences.clearPackagePreferences(context, packageName);
             }
         } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
             if (packageName != null) {
-                resolver.call(RecentsProvider.buildRecent(), RecentsProvider.METHOD_PURGE_PACKAGE,
+                resolver.call(
+                        LastAccessedProvider.buildLastAccessed(packageName),
+                        LastAccessedProvider.METHOD_PURGE_PACKAGE,
                         packageName, null);
                 LocalPreferences.clearPackagePreferences(context, packageName);
             }
diff --git a/src/com/android/documentsui/RecentsCreateFragment.java b/src/com/android/documentsui/RecentsCreateFragment.java
deleted file mode 100644
index 019ca86..0000000
--- a/src/com/android/documentsui/RecentsCreateFragment.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.android.documentsui;
-
-import static com.android.documentsui.Shared.TAG;
-import static com.android.documentsui.State.ACTION_CREATE;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils.TruncateAt;
-import android.text.style.ImageSpan;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.documentsui.Events.MotionInputEvent;
-import com.android.documentsui.RecentsProvider.RecentColumns;
-import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.DurableUtils;
-import com.android.documentsui.model.RootInfo;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import libcore.io.IoUtils;
-
-/**
- * Display directories where recent creates took place.
- */
-public class RecentsCreateFragment extends Fragment {
-
-    private View mEmptyView;
-    private RecyclerView mRecView;
-    private DocumentStackAdapter mAdapter;
-    private LoaderCallbacks<List<DocumentStack>> mCallbacks;
-
-    private static final int LOADER_RECENTS = 3;
-
-    public static void show(FragmentManager fm) {
-        final RecentsCreateFragment fragment = new RecentsCreateFragment();
-        final FragmentTransaction ft = fm.beginTransaction();
-        ft.replace(R.id.container_directory, fragment);
-        ft.commitAllowingStateLoss();
-    }
-
-    @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        final Context context = inflater.getContext();
-
-        final View view = inflater.inflate(R.layout.fragment_directory, container, false);
-
-        mRecView = (RecyclerView) view.findViewById(R.id.dir_list);
-        mRecView.setLayoutManager(new LinearLayoutManager(getContext()));
-        mRecView.addOnItemTouchListener(mItemListener);
-
-        mEmptyView = view.findViewById(android.R.id.empty);
-
-        mAdapter = new DocumentStackAdapter();
-        mRecView.setAdapter(mAdapter);
-
-        final RootsCache roots = DocumentsApplication.getRootsCache(context);
-        final State state = ((BaseActivity) getActivity()).getDisplayState();
-
-        mCallbacks = new LoaderCallbacks<List<DocumentStack>>() {
-            @Override
-            public Loader<List<DocumentStack>> onCreateLoader(int id, Bundle args) {
-                return new RecentsCreateLoader(context, roots, state);
-            }
-
-            @Override
-            public void onLoadFinished(
-                    Loader<List<DocumentStack>> loader, List<DocumentStack> data) {
-                mAdapter.update(data);
-
-                // When launched into empty recents, show drawer
-                if (mAdapter.isEmpty() && !state.hasLocationChanged()
-                        && state.action != ACTION_CREATE
-                        && context instanceof DocumentsActivity) {
-                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
-                }
-            }
-
-            @Override
-            public void onLoaderReset(Loader<List<DocumentStack>> loader) {
-                mAdapter.update(null);
-            }
-        };
-
-        return view;
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        getLoaderManager().restartLoader(LOADER_RECENTS, getArguments(), mCallbacks);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        getLoaderManager().destroyLoader(LOADER_RECENTS);
-    }
-
-    private RecyclerView.OnItemTouchListener mItemListener =
-            new RecyclerView.OnItemTouchListener() {
-                @Override
-                public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
-                    try (MotionInputEvent event = MotionInputEvent.obtain(e, mRecView)) {
-                        if (event.isOverItem() && event.isActionUp()) {
-                            final DocumentStack stack = mAdapter.getItem(event.getItemPosition());
-                            ((BaseActivity) getActivity()).onStackPicked(stack);
-                            return true;
-                        }
-                        return false;
-                    }
-                }
-
-                @Override
-                public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
-                @Override
-                public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
-            };
-
-    public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> {
-        private final RootsCache mRoots;
-        private final State mState;
-
-        public RecentsCreateLoader(Context context, RootsCache roots, State state) {
-            super(context, RecentsProvider.buildRecent());
-            mRoots = roots;
-            mState = state;
-        }
-
-        @Override
-        public List<DocumentStack> loadInBackground(Uri uri, CancellationSignal signal) {
-            final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
-            final ArrayList<DocumentStack> result = new ArrayList<>();
-
-            final ContentResolver resolver = getContext().getContentResolver();
-            final Cursor cursor = resolver.query(
-                    uri, null, null, null, RecentColumns.TIMESTAMP + " DESC", signal);
-            try {
-                while (cursor != null && cursor.moveToNext()) {
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(RecentColumns.STACK));
-                    try {
-                        final DocumentStack stack = new DocumentStack();
-                        DurableUtils.readFromArray(rawStack, stack);
-
-                        // Only update root here to avoid spinning up all
-                        // providers; we update the stack during the actual
-                        // restore. This also filters away roots that don't
-                        // match current filter.
-                        stack.updateRoot(matchingRoots);
-                        result.add(stack);
-                    } catch (IOException e) {
-                        Log.w(TAG, "Failed to resolve stack: " + e);
-                    }
-                }
-            } finally {
-                IoUtils.closeQuietly(cursor);
-            }
-
-            return result;
-        }
-    }
-
-    private static final class StackHolder extends RecyclerView.ViewHolder {
-        public View view;
-        public StackHolder(View view) {
-            super(view);
-            this.view = view;
-        }
-    }
-
-    private class DocumentStackAdapter extends RecyclerView.Adapter<StackHolder> {
-        @Nullable private List<DocumentStack> mItems;
-
-        DocumentStack getItem(int position) {
-            return mItems.get(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            return mItems == null ? 0 : mItems.size();
-        }
-
-        boolean isEmpty() {
-            return mItems == null ? true : mItems.isEmpty();
-        }
-
-        void update(@Nullable List<DocumentStack> items) {
-            mItems = items;
-
-            if (isEmpty()) {
-                mEmptyView.setVisibility(View.VISIBLE);
-            } else {
-                mEmptyView.setVisibility(View.GONE);
-            }
-
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public StackHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-          final Context context = parent.getContext();
-
-          final LayoutInflater inflater = LayoutInflater.from(context);
-          return new StackHolder(
-                  inflater.inflate(R.layout.item_doc_list, parent, false));
-        }
-
-        @Override
-        public void onBindViewHolder(StackHolder holder, int position) {
-            Context context = getContext();
-            View view = holder.view;
-
-            final ImageView iconMime = (ImageView) view.findViewById(R.id.icon_mime);
-            final TextView title = (TextView) view.findViewById(android.R.id.title);
-            final View line2 = view.findViewById(R.id.line2);
-
-            final DocumentStack stack = getItem(position);
-            iconMime.setImageDrawable(stack.root.loadIcon(context));
-
-            final Drawable crumb = context.getDrawable(R.drawable.ic_breadcrumb_arrow);
-            crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight());
-
-            final SpannableStringBuilder builder = new SpannableStringBuilder();
-            builder.append(stack.root.title);
-            for (int i = stack.size() - 2; i >= 0; i--) {
-                appendDrawable(builder, crumb);
-                builder.append(stack.get(i).displayName);
-            }
-            title.setText(builder);
-            title.setEllipsize(TruncateAt.MIDDLE);
-
-            if (line2 != null) line2.setVisibility(View.GONE);
-        }
-    }
-
-    private static void appendDrawable(SpannableStringBuilder b, Drawable d) {
-        final int length = b.length();
-        b.append("\u232a");
-        b.setSpan(new ImageSpan(d), length, b.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-    }
-}
diff --git a/src/com/android/documentsui/RecentsProvider.java b/src/com/android/documentsui/RecentsProvider.java
deleted file mode 100644
index 6ef9154..0000000
--- a/src/com/android/documentsui/RecentsProvider.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2013 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 com.android.documentsui;
-
-import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.UriMatcher;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
-import android.provider.DocumentsContract.Root;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import com.android.documentsui.model.DocumentStack;
-import com.android.documentsui.model.DurableUtils;
-import com.android.internal.util.Predicate;
-
-import com.google.android.collect.Sets;
-
-import libcore.io.IoUtils;
-
-import java.io.IOException;
-import java.util.Set;
-
-public class RecentsProvider extends ContentProvider {
-    private static final String TAG = "RecentsProvider";
-
-    private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
-
-    private static final String AUTHORITY = "com.android.documentsui.recents";
-
-    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
-    private static final int URI_RECENT = 1;
-    private static final int URI_STATE = 2;
-    private static final int URI_RESUME = 3;
-
-    public static final String METHOD_PURGE = "purge";
-    public static final String METHOD_PURGE_PACKAGE = "purgePackage";
-
-    static {
-        sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
-        // state/authority/rootId/docId
-        sMatcher.addURI(AUTHORITY, "state/*/*/*", URI_STATE);
-        // resume/packageName
-        sMatcher.addURI(AUTHORITY, "resume/*", URI_RESUME);
-    }
-
-    public static final String TABLE_RECENT = "recent";
-    public static final String TABLE_STATE = "state";
-    public static final String TABLE_RESUME = "resume";
-
-    public static class RecentColumns {
-        public static final String KEY = "key";
-        public static final String STACK = "stack";
-        public static final String TIMESTAMP = "timestamp";
-    }
-
-    public static class StateColumns {
-        public static final String AUTHORITY = "authority";
-        public static final String ROOT_ID = Root.COLUMN_ROOT_ID;
-        public static final String DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID;
-
-        @Deprecated  // mode is tracked in local preferences now...by root only
-        public static final String MODE = "mode";
-        public static final String SORT_ORDER = "sortOrder";
-    }
-
-    public static class ResumeColumns {
-        public static final String PACKAGE_NAME = "package_name";
-        public static final String STACK = "stack";
-        public static final String TIMESTAMP = "timestamp";
-        // Indicates handler was an external app, like photos.
-        public static final String EXTERNAL = "external";
-    }
-
-    public static Uri buildRecent() {
-        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(AUTHORITY).appendPath("recent").build();
-    }
-
-    public static Uri buildState(String authority, String rootId, String documentId) {
-        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
-                .appendPath("state").appendPath(authority).appendPath(rootId).appendPath(documentId)
-                .build();
-    }
-
-    public static Uri buildResume(String packageName) {
-        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(AUTHORITY).appendPath("resume").appendPath(packageName).build();
-    }
-
-    private DatabaseHelper mHelper;
-
-    private static class DatabaseHelper extends SQLiteOpenHelper {
-        private static final String DB_NAME = "recents.db";
-
-        private static final int VERSION_INIT = 1;
-        private static final int VERSION_AS_BLOB = 3;
-        private static final int VERSION_ADD_EXTERNAL = 4;
-        private static final int VERSION_ADD_RECENT_KEY = 5;
-
-        public DatabaseHelper(Context context) {
-            super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-
-            db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
-                    RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
-                    RecentColumns.STACK + " BLOB DEFAULT NULL," +
-                    RecentColumns.TIMESTAMP + " INTEGER" +
-                    ")");
-
-            db.execSQL("CREATE TABLE " + TABLE_STATE + " (" +
-                    StateColumns.AUTHORITY + " TEXT," +
-                    StateColumns.ROOT_ID + " TEXT," +
-                    StateColumns.DOCUMENT_ID + " TEXT," +
-                    StateColumns.MODE + " INTEGER," +
-                    StateColumns.SORT_ORDER + " INTEGER," +
-                    "PRIMARY KEY (" + StateColumns.AUTHORITY + ", " + StateColumns.ROOT_ID + ", "
-                    + StateColumns.DOCUMENT_ID + ")" +
-                    ")");
-
-            db.execSQL("CREATE TABLE " + TABLE_RESUME + " (" +
-                    ResumeColumns.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY," +
-                    ResumeColumns.STACK + " BLOB DEFAULT NULL," +
-                    ResumeColumns.TIMESTAMP + " INTEGER," +
-                    ResumeColumns.EXTERNAL + " INTEGER NOT NULL DEFAULT 0" +
-                    ")");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            Log.w(TAG, "Upgrading database; wiping app data");
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECENT);
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_STATE);
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_RESUME);
-            onCreate(db);
-        }
-    }
-
-    @Override
-    public boolean onCreate() {
-        mHelper = new DatabaseHelper(getContext());
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        final SQLiteDatabase db = mHelper.getReadableDatabase();
-        switch (sMatcher.match(uri)) {
-            case URI_RECENT:
-                final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
-                return db.query(TABLE_RECENT, projection, RecentColumns.TIMESTAMP + ">" + cutoff,
-                        null, null, null, sortOrder);
-            case URI_STATE:
-                final String authority = uri.getPathSegments().get(1);
-                final String rootId = uri.getPathSegments().get(2);
-                final String documentId = uri.getPathSegments().get(3);
-                return db.query(TABLE_STATE, projection, StateColumns.AUTHORITY + "=? AND "
-                        + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
-                        new String[] { authority, rootId, documentId }, null, null, sortOrder);
-            case URI_RESUME:
-                final String packageName = uri.getPathSegments().get(1);
-                return db.query(TABLE_RESUME, projection, ResumeColumns.PACKAGE_NAME + "=?",
-                        new String[] { packageName }, null, null, sortOrder);
-            default:
-                throw new UnsupportedOperationException("Unsupported Uri " + uri);
-        }
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        final SQLiteDatabase db = mHelper.getWritableDatabase();
-        final ContentValues key = new ContentValues();
-        switch (sMatcher.match(uri)) {
-            case URI_RECENT:
-                values.put(RecentColumns.TIMESTAMP, System.currentTimeMillis());
-                db.insert(TABLE_RECENT, null, values);
-                final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
-                db.delete(TABLE_RECENT, RecentColumns.TIMESTAMP + "<" + cutoff, null);
-                return uri;
-            case URI_STATE:
-                final String authority = uri.getPathSegments().get(1);
-                final String rootId = uri.getPathSegments().get(2);
-                final String documentId = uri.getPathSegments().get(3);
-
-                key.put(StateColumns.AUTHORITY, authority);
-                key.put(StateColumns.ROOT_ID, rootId);
-                key.put(StateColumns.DOCUMENT_ID, documentId);
-
-                // Ensure that row exists, then update with changed values
-                db.insertWithOnConflict(TABLE_STATE, null, key, SQLiteDatabase.CONFLICT_IGNORE);
-                db.update(TABLE_STATE, values, StateColumns.AUTHORITY + "=? AND "
-                        + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
-                        new String[] { authority, rootId, documentId });
-
-                return uri;
-            case URI_RESUME:
-                values.put(ResumeColumns.TIMESTAMP, System.currentTimeMillis());
-
-                final String packageName = uri.getPathSegments().get(1);
-                key.put(ResumeColumns.PACKAGE_NAME, packageName);
-
-                // Ensure that row exists, then update with changed values
-                db.insertWithOnConflict(TABLE_RESUME, null, key, SQLiteDatabase.CONFLICT_IGNORE);
-                db.update(TABLE_RESUME, values, ResumeColumns.PACKAGE_NAME + "=?",
-                        new String[] { packageName });
-                return uri;
-            default:
-                throw new UnsupportedOperationException("Unsupported Uri " + uri);
-        }
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("Unsupported Uri " + uri);
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("Unsupported Uri " + uri);
-    }
-
-    @Override
-    public Bundle call(String method, String arg, Bundle extras) {
-        if (METHOD_PURGE.equals(method)) {
-            // Purge references to unknown authorities
-            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
-            final Set<String> knownAuth = Sets.newHashSet();
-            for (ResolveInfo info : getContext()
-                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
-                knownAuth.add(info.providerInfo.authority);
-            }
-
-            purgeByAuthority(new Predicate<String>() {
-                @Override
-                public boolean apply(String authority) {
-                    // Purge unknown authorities
-                    return !knownAuth.contains(authority);
-                }
-            });
-
-            return null;
-
-        } else if (METHOD_PURGE_PACKAGE.equals(method)) {
-            // Purge references to authorities in given package
-            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
-            intent.setPackage(arg);
-            final Set<String> packageAuth = Sets.newHashSet();
-            for (ResolveInfo info : getContext()
-                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
-                packageAuth.add(info.providerInfo.authority);
-            }
-
-            if (!packageAuth.isEmpty()) {
-                purgeByAuthority(new Predicate<String>() {
-                    @Override
-                    public boolean apply(String authority) {
-                        // Purge authority matches
-                        return packageAuth.contains(authority);
-                    }
-                });
-            }
-
-            return null;
-
-        } else {
-            return super.call(method, arg, extras);
-        }
-    }
-
-    /**
-     * Purge all internal data whose authority matches the given
-     * {@link Predicate}.
-     */
-    private void purgeByAuthority(Predicate<String> predicate) {
-        final SQLiteDatabase db = mHelper.getWritableDatabase();
-        final DocumentStack stack = new DocumentStack();
-
-        Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null);
-        try {
-            while (cursor.moveToNext()) {
-                try {
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(RecentColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, stack);
-
-                    if (stack.root != null && predicate.apply(stack.root.authority)) {
-                        final String key = getCursorString(cursor, RecentColumns.KEY);
-                        db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key });
-                    }
-                } catch (IOException ignored) {
-                }
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-
-        cursor = db.query(TABLE_STATE, new String[] {
-                StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null);
-        try {
-            while (cursor.moveToNext()) {
-                final String authority = getCursorString(cursor, StateColumns.AUTHORITY);
-                if (predicate.apply(authority)) {
-                    db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
-                            authority });
-                    if (DEBUG) Log.d(TAG, "Purged state for " + authority);
-                }
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-
-        cursor = db.query(TABLE_RESUME, null, null, null, null, null, null);
-        try {
-            while (cursor.moveToNext()) {
-                try {
-                    final byte[] rawStack = cursor.getBlob(
-                            cursor.getColumnIndex(ResumeColumns.STACK));
-                    DurableUtils.readFromArray(rawStack, stack);
-
-                    if (stack.root != null && predicate.apply(stack.root.authority)) {
-                        final String packageName = getCursorString(
-                                cursor, ResumeColumns.PACKAGE_NAME);
-                        db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?",
-                                new String[] { packageName });
-                    }
-                } catch (IOException ignored) {
-                }
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-    }
-}
diff --git a/src/com/android/documentsui/RootsCache.java b/src/com/android/documentsui/RootsCache.java
index 117bb01..e6fe31b 100644
--- a/src/com/android/documentsui/RootsCache.java
+++ b/src/com/android/documentsui/RootsCache.java
@@ -96,8 +96,7 @@
                 // Special root for recents
                 derivedIcon = R.drawable.ic_root_recent;
                 derivedType = RootInfo.TYPE_RECENTS;
-                flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD
-                        | Root.FLAG_SUPPORTS_CREATE;
+                flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD;
                 title = mContext.getString(R.string.root_recent);
                 availableBytes = -1;
             }};
@@ -134,9 +133,7 @@
         assert(mRecentsRoot.rootId == null);
         assert(mRecentsRoot.derivedIcon == R.drawable.ic_root_recent);
         assert(mRecentsRoot.derivedType == RootInfo.TYPE_RECENTS);
-        assert(mRecentsRoot.flags == (Root.FLAG_LOCAL_ONLY
-                | Root.FLAG_SUPPORTS_IS_CHILD
-                | Root.FLAG_SUPPORTS_CREATE));
+        assert(mRecentsRoot.flags == (Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_IS_CHILD));
         assert(mRecentsRoot.availableBytes == -1);
 
         new UpdateTask(forceRefreshAll, null)