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