192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey/*
292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * Copyright (C) 2013 The Android Open Source Project
392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey *
492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * you may not use this file except in compliance with the License.
692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * You may obtain a copy of the License at
792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey *
892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey *
1092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * Unless required by applicable law or agreed to in writing, software
1192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
1292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * See the License for the specific language governing permissions and
1492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey * limitations under the License.
1592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey */
1692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
1792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeypackage com.android.documentsui;
1892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
19758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport static com.android.documentsui.model.DocumentInfo.getCursorString;
20758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
2192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.content.ContentProvider;
22dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkeyimport android.content.ContentResolver;
2392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.content.ContentValues;
2492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.content.Context;
25758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport android.content.Intent;
2692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.content.UriMatcher;
27758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport android.content.pm.ResolveInfo;
2892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.database.Cursor;
2992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.database.sqlite.SQLiteDatabase;
3092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.database.sqlite.SQLiteOpenHelper;
3192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.net.Uri;
32758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport android.os.Bundle;
33758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport android.provider.DocumentsContract;
34d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkeyimport android.provider.DocumentsContract.Document;
35d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkeyimport android.provider.DocumentsContract.Root;
3692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.text.format.DateUtils;
3792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeyimport android.util.Log;
3892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
39758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport com.android.documentsui.model.DocumentStack;
40758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport com.android.documentsui.model.DurableUtils;
41758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport com.android.internal.util.Predicate;
42758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport com.google.android.collect.Sets;
43758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
44758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport libcore.io.IoUtils;
45758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
46758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport java.io.IOException;
47758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkeyimport java.util.Set;
48758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
4992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkeypublic class RecentsProvider extends ContentProvider {
5092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    private static final String TAG = "RecentsProvider";
5192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
52758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    private static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
53dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
54d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    private static final String AUTHORITY = "com.android.documentsui.recents";
5592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
5692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
5792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
58d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    private static final int URI_RECENT = 1;
59d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    private static final int URI_STATE = 2;
6092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    private static final int URI_RESUME = 3;
6192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
62758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    public static final String METHOD_PURGE = "purge";
63758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    public static final String METHOD_PURGE_PACKAGE = "purgePackage";
64758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
6592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    static {
66d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        sMatcher.addURI(AUTHORITY, "recent", URI_RECENT);
67d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        // state/authority/rootId/docId
68d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        sMatcher.addURI(AUTHORITY, "state/*/*/*", URI_STATE);
69d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        // resume/packageName
7092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        sMatcher.addURI(AUTHORITY, "resume/*", URI_RESUME);
7192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
7292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
73d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static final String TABLE_RECENT = "recent";
74d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static final String TABLE_STATE = "state";
75d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static final String TABLE_RESUME = "resume";
76d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
77d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static class RecentColumns {
786efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        public static final String KEY = "key";
79d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String STACK = "stack";
80d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String TIMESTAMP = "timestamp";
81d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    }
82d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
83d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static class StateColumns {
84d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String AUTHORITY = "authority";
85d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String ROOT_ID = Root.COLUMN_ROOT_ID;
86d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID;
87d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String MODE = "mode";
88d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String SORT_ORDER = "sortOrder";
89dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    }
90dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
91d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static class ResumeColumns {
92d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String PACKAGE_NAME = "package_name";
93d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String STACK = "stack";
94d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        public static final String TIMESTAMP = "timestamp";
95deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey        public static final String EXTERNAL = "external";
96d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    }
97d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
98d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static Uri buildRecent() {
99dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
100d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                .authority(AUTHORITY).appendPath("recent").build();
101d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    }
102d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
103d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey    public static Uri buildState(String authority, String rootId, String documentId) {
104d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
105d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                .appendPath("state").appendPath(authority).appendPath(rootId).appendPath(documentId)
106d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                .build();
107dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    }
108dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
109dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    public static Uri buildResume(String packageName) {
110dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
111dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey                .authority(AUTHORITY).appendPath("resume").appendPath(packageName).build();
112dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey    }
113dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey
11492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    private DatabaseHelper mHelper;
11592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
11692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    private static class DatabaseHelper extends SQLiteOpenHelper {
117dc2963aecaf38bf53d6de82957412a486049c207Jeff Sharkey        private static final String DB_NAME = "recents.db";
11892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
11992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        private static final int VERSION_INIT = 1;
120d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey        private static final int VERSION_AS_BLOB = 3;
121deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey        private static final int VERSION_ADD_EXTERNAL = 4;
1226efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey        private static final int VERSION_ADD_RECENT_KEY = 5;
12392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
12492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        public DatabaseHelper(Context context) {
1256efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey            super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY);
12692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        }
12792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
12892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        @Override
12992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        public void onCreate(SQLiteDatabase db) {
130d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
131d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
1326efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey                    RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
1336efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey                    RecentColumns.STACK + " BLOB DEFAULT NULL," +
134d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    RecentColumns.TIMESTAMP + " INTEGER" +
13592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                    ")");
13692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
137d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            db.execSQL("CREATE TABLE " + TABLE_STATE + " (" +
138d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    StateColumns.AUTHORITY + " TEXT," +
139d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    StateColumns.ROOT_ID + " TEXT," +
140d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    StateColumns.DOCUMENT_ID + " TEXT," +
141d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    StateColumns.MODE + " INTEGER," +
142d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    StateColumns.SORT_ORDER + " INTEGER," +
143d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    "PRIMARY KEY (" + StateColumns.AUTHORITY + ", " + StateColumns.ROOT_ID + ", "
144d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                    + StateColumns.DOCUMENT_ID + ")" +
14592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                    ")");
14692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
14792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey            db.execSQL("CREATE TABLE " + TABLE_RESUME + " (" +
148deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                    ResumeColumns.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY," +
149deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                    ResumeColumns.STACK + " BLOB DEFAULT NULL," +
150deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                    ResumeColumns.TIMESTAMP + " INTEGER," +
151deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                    ResumeColumns.EXTERNAL + " INTEGER NOT NULL DEFAULT 0" +
15292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                    ")");
15392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        }
15492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
15592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        @Override
15692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
15792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey            Log.w(TAG, "Upgrading database; wiping app data");
158d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            db.execSQL("DROP TABLE IF EXISTS " + TABLE_RECENT);
159d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            db.execSQL("DROP TABLE IF EXISTS " + TABLE_STATE);
16092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey            db.execSQL("DROP TABLE IF EXISTS " + TABLE_RESUME);
16192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey            onCreate(db);
16292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        }
16392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
16492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
16592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
16692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public boolean onCreate() {
16792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        mHelper = new DatabaseHelper(getContext());
16892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        return true;
16992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
17092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
17192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
17292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
17392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey            String sortOrder) {
17492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        final SQLiteDatabase db = mHelper.getReadableDatabase();
17592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        switch (sMatcher.match(uri)) {
176d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_RECENT:
1773f4c205fd3110345241e690f2a2e7c1b477eac76Jeff Sharkey                final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
1783f4c205fd3110345241e690f2a2e7c1b477eac76Jeff Sharkey                return db.query(TABLE_RECENT, projection, RecentColumns.TIMESTAMP + ">" + cutoff,
1794ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        null, null, null, sortOrder);
180d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_STATE:
181d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String authority = uri.getPathSegments().get(1);
182d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String rootId = uri.getPathSegments().get(2);
183d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String documentId = uri.getPathSegments().get(3);
184d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                return db.query(TABLE_STATE, projection, StateColumns.AUTHORITY + "=? AND "
185d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                        + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
1864ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        new String[] { authority, rootId, documentId }, null, null, sortOrder);
187d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_RESUME:
18892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                final String packageName = uri.getPathSegments().get(1);
189d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                return db.query(TABLE_RESUME, projection, ResumeColumns.PACKAGE_NAME + "=?",
1904ec973925fc2cd18f9ec0d0ca5af588564fded27Jeff Sharkey                        new String[] { packageName }, null, null, sortOrder);
191d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            default:
19292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                throw new UnsupportedOperationException("Unsupported Uri " + uri);
19392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        }
19492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
19592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
19692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
19792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public String getType(Uri uri) {
19892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        return null;
19992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
20092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
20192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
20292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public Uri insert(Uri uri, ContentValues values) {
20392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        final SQLiteDatabase db = mHelper.getWritableDatabase();
204deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey        final ContentValues key = new ContentValues();
20592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        switch (sMatcher.match(uri)) {
206d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_RECENT:
207d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                values.put(RecentColumns.TIMESTAMP, System.currentTimeMillis());
208d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                db.insert(TABLE_RECENT, null, values);
2093f4c205fd3110345241e690f2a2e7c1b477eac76Jeff Sharkey                final long cutoff = System.currentTimeMillis() - MAX_HISTORY_IN_MILLIS;
2103f4c205fd3110345241e690f2a2e7c1b477eac76Jeff Sharkey                db.delete(TABLE_RECENT, RecentColumns.TIMESTAMP + "<" + cutoff, null);
21192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                return uri;
212d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_STATE:
213d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String authority = uri.getPathSegments().get(1);
214d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String rootId = uri.getPathSegments().get(2);
215d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                final String documentId = uri.getPathSegments().get(3);
216d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
217d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                key.put(StateColumns.AUTHORITY, authority);
218d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                key.put(StateColumns.ROOT_ID, rootId);
219d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                key.put(StateColumns.DOCUMENT_ID, documentId);
220d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
221d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                // Ensure that row exists, then update with changed values
222d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                db.insertWithOnConflict(TABLE_STATE, null, key, SQLiteDatabase.CONFLICT_IGNORE);
223d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                db.update(TABLE_STATE, values, StateColumns.AUTHORITY + "=? AND "
224d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                        + StateColumns.ROOT_ID + "=? AND " + StateColumns.DOCUMENT_ID + "=?",
225d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                        new String[] { authority, rootId, documentId });
226d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey
22792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                return uri;
228d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            case URI_RESUME:
229d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey                values.put(ResumeColumns.TIMESTAMP, System.currentTimeMillis());
230deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey
231deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                final String packageName = uri.getPathSegments().get(1);
232deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                key.put(ResumeColumns.PACKAGE_NAME, packageName);
233deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey
234deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                // Ensure that row exists, then update with changed values
235deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                db.insertWithOnConflict(TABLE_RESUME, null, key, SQLiteDatabase.CONFLICT_IGNORE);
236deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                db.update(TABLE_RESUME, values, ResumeColumns.PACKAGE_NAME + "=?",
237deffadeb7485e8660ecce12822e259d96fa06dceJeff Sharkey                        new String[] { packageName });
23892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                return uri;
239d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey            default:
24092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey                throw new UnsupportedOperationException("Unsupported Uri " + uri);
24192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        }
24292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
24392d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
24492d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
24592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
24692d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        throw new UnsupportedOperationException("Unsupported Uri " + uri);
24792d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
24892d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey
24992d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    @Override
25092d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    public int delete(Uri uri, String selection, String[] selectionArgs) {
25192d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey        throw new UnsupportedOperationException("Unsupported Uri " + uri);
25292d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey    }
253758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
254758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    @Override
255758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    public Bundle call(String method, String arg, Bundle extras) {
256758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        if (METHOD_PURGE.equals(method)) {
257758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            // Purge references to unknown authorities
258758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
259758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            final Set<String> knownAuth = Sets.newHashSet();
260758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            for (ResolveInfo info : getContext()
261758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
262758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                knownAuth.add(info.providerInfo.authority);
263758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
264758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
265758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            purgeByAuthority(new Predicate<String>() {
266758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                @Override
267758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                public boolean apply(String authority) {
268758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    // Purge unknown authorities
269758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    return !knownAuth.contains(authority);
270758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                }
271758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            });
272758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
273758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            return null;
274758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
275758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        } else if (METHOD_PURGE_PACKAGE.equals(method)) {
276758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            // Purge references to authorities in given package
277758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
278758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            intent.setPackage(arg);
279758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            final Set<String> packageAuth = Sets.newHashSet();
280758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            for (ResolveInfo info : getContext()
281758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    .getPackageManager().queryIntentContentProviders(intent, 0)) {
282758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                packageAuth.add(info.providerInfo.authority);
283758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
284758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
285758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            if (!packageAuth.isEmpty()) {
286758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                purgeByAuthority(new Predicate<String>() {
287758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    @Override
288758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    public boolean apply(String authority) {
289758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        // Purge authority matches
290758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        return packageAuth.contains(authority);
291758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    }
292758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                });
293758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
294758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
295758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            return null;
296758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
297758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        } else {
298758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            return super.call(method, arg, extras);
299758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        }
300758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    }
301758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
302758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    /**
303758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey     * Purge all internal data whose authority matches the given
304758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey     * {@link Predicate}.
305758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey     */
306758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    private void purgeByAuthority(Predicate<String> predicate) {
307758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        final SQLiteDatabase db = mHelper.getWritableDatabase();
308758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        final DocumentStack stack = new DocumentStack();
309758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
310758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        Cursor cursor = db.query(TABLE_RECENT, null, null, null, null, null, null);
311758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        try {
312758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            while (cursor.moveToNext()) {
313758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                try {
314758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    final byte[] rawStack = cursor.getBlob(
315758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                            cursor.getColumnIndex(RecentColumns.STACK));
316758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    DurableUtils.readFromArray(rawStack, stack);
317758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
318758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    if (stack.root != null && predicate.apply(stack.root.authority)) {
319758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        final String key = getCursorString(cursor, RecentColumns.KEY);
320758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key });
321758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    }
322758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                } catch (IOException ignored) {
323758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                }
324758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
325758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        } finally {
326758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            IoUtils.closeQuietly(cursor);
327758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        }
328758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
329758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        cursor = db.query(TABLE_STATE, new String[] {
330758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                StateColumns.AUTHORITY }, null, null, StateColumns.AUTHORITY, null, null);
331758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        try {
332758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            while (cursor.moveToNext()) {
333758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                final String authority = getCursorString(cursor, StateColumns.AUTHORITY);
334758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                if (predicate.apply(authority)) {
335758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] {
336758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                            authority });
337758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    Log.d(TAG, "Purged state for " + authority);
338758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                }
339758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
340758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        } finally {
341758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            IoUtils.closeQuietly(cursor);
342758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        }
343758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
344758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        cursor = db.query(TABLE_RESUME, null, null, null, null, null, null);
345758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        try {
346758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            while (cursor.moveToNext()) {
347758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                try {
348758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    final byte[] rawStack = cursor.getBlob(
349758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                            cursor.getColumnIndex(ResumeColumns.STACK));
350758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    DurableUtils.readFromArray(rawStack, stack);
351758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey
352758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    if (stack.root != null && predicate.apply(stack.root.authority)) {
353758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        final String packageName = getCursorString(
354758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                                cursor, ResumeColumns.PACKAGE_NAME);
355758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                        db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?",
356758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                                new String[] { packageName });
357758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                    }
358758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                } catch (IOException ignored) {
359758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey                }
360758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            }
361758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        } finally {
362758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey            IoUtils.closeQuietly(cursor);
363758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey        }
364758f97e46df203659c46941dc4483f7b369a5640Jeff Sharkey    }
36592d7e697a864a3e18bef4ef256bb3eb339a66b4eJeff Sharkey}
366