• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.browser.provider;
17 
18 import android.content.ContentProvider;
19 import android.content.ContentUris;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.content.UriMatcher;
23 import android.database.Cursor;
24 import android.database.DatabaseUtils;
25 import android.database.sqlite.SQLiteDatabase;
26 import android.database.sqlite.SQLiteOpenHelper;
27 import android.database.sqlite.SQLiteQueryBuilder;
28 import android.net.Uri;
29 import android.os.FileUtils;
30 import android.provider.BrowserContract;
31 import android.text.TextUtils;
32 
33 import java.io.File;
34 
35 public class SnapshotProvider extends ContentProvider {
36 
37     public static interface Snapshots {
38 
39         public static final Uri CONTENT_URI = Uri.withAppendedPath(
40                 SnapshotProvider.AUTHORITY_URI, "snapshots");
41         public static final String _ID = "_id";
42         @Deprecated
43         public static final String VIEWSTATE = "view_state";
44         public static final String BACKGROUND = "background";
45         public static final String TITLE = "title";
46         public static final String URL = "url";
47         public static final String FAVICON = "favicon";
48         public static final String THUMBNAIL = "thumbnail";
49         public static final String DATE_CREATED = "date_created";
50         public static final String VIEWSTATE_PATH = "viewstate_path";
51         public static final String VIEWSTATE_SIZE = "viewstate_size";
52     }
53 
54     public static final String AUTHORITY = "com.android.browser.snapshots";
55     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
56 
57     static final String TABLE_SNAPSHOTS = "snapshots";
58     static final int SNAPSHOTS = 10;
59     static final int SNAPSHOTS_ID = 11;
60     static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
61     // Workaround that we can't remove the "NOT NULL" constraint on VIEWSTATE
62     static final byte[] NULL_BLOB_HACK = new byte[0];
63 
64     SnapshotDatabaseHelper mOpenHelper;
65 
66     static {
URI_MATCHER.addURI(AUTHORITY, "snapshots", SNAPSHOTS)67         URI_MATCHER.addURI(AUTHORITY, "snapshots", SNAPSHOTS);
URI_MATCHER.addURI(AUTHORITY, "snapshots/#", SNAPSHOTS_ID)68         URI_MATCHER.addURI(AUTHORITY, "snapshots/#", SNAPSHOTS_ID);
69     }
70 
71     final static class SnapshotDatabaseHelper extends SQLiteOpenHelper {
72 
73         static final String DATABASE_NAME = "snapshots.db";
74         static final int DATABASE_VERSION = 3;
75 
SnapshotDatabaseHelper(Context context)76         public SnapshotDatabaseHelper(Context context) {
77             super(context, DATABASE_NAME, null, DATABASE_VERSION);
78         }
79 
80         @Override
onCreate(SQLiteDatabase db)81         public void onCreate(SQLiteDatabase db) {
82             db.execSQL("CREATE TABLE " + TABLE_SNAPSHOTS + "(" +
83                     Snapshots._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
84                     Snapshots.TITLE + " TEXT," +
85                     Snapshots.URL + " TEXT NOT NULL," +
86                     Snapshots.DATE_CREATED + " INTEGER," +
87                     Snapshots.FAVICON + " BLOB," +
88                     Snapshots.THUMBNAIL + " BLOB," +
89                     Snapshots.BACKGROUND + " INTEGER," +
90                     Snapshots.VIEWSTATE + " BLOB NOT NULL," +
91                     Snapshots.VIEWSTATE_PATH + " TEXT," +
92                     Snapshots.VIEWSTATE_SIZE + " INTEGER" +
93                     ");");
94         }
95 
96         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)97         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
98             if (oldVersion < 2) {
99                 db.execSQL("DROP TABLE " + TABLE_SNAPSHOTS);
100                 onCreate(db);
101             }
102             if (oldVersion < 3) {
103                 db.execSQL("ALTER TABLE " + TABLE_SNAPSHOTS + " ADD COLUMN "
104                         + Snapshots.VIEWSTATE_PATH + " TEXT");
105                 db.execSQL("ALTER TABLE " + TABLE_SNAPSHOTS + " ADD COLUMN "
106                         + Snapshots.VIEWSTATE_SIZE + " INTEGER");
107                 db.execSQL("UPDATE " + TABLE_SNAPSHOTS + " SET "
108                         + Snapshots.VIEWSTATE_SIZE + " = length("
109                         + Snapshots.VIEWSTATE + ")");
110             }
111         }
112 
113     }
114 
getOldDatabasePath(Context context)115     static File getOldDatabasePath(Context context) {
116         File dir = context.getExternalFilesDir(null);
117         return new File(dir, SnapshotDatabaseHelper.DATABASE_NAME);
118     }
119 
migrateToDataFolder()120     private void migrateToDataFolder() {
121         File dbPath = getContext().getDatabasePath(SnapshotDatabaseHelper.DATABASE_NAME);
122         if (dbPath.exists()) return;
123         File oldPath = getOldDatabasePath(getContext());
124         if (oldPath.exists()) {
125             // Try to move
126             if (!oldPath.renameTo(dbPath)) {
127                 // Failed, do a copy
128                 FileUtils.copyFile(oldPath, dbPath);
129             }
130             // Cleanup
131             oldPath.delete();
132         }
133     }
134 
135     @Override
onCreate()136     public boolean onCreate() {
137         migrateToDataFolder();
138         mOpenHelper = new SnapshotDatabaseHelper(getContext());
139         return true;
140     }
141 
getWritableDatabase()142     SQLiteDatabase getWritableDatabase() {
143         return mOpenHelper.getWritableDatabase();
144     }
145 
getReadableDatabase()146     SQLiteDatabase getReadableDatabase() {
147         return mOpenHelper.getReadableDatabase();
148     }
149 
150     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)151     public Cursor query(Uri uri, String[] projection, String selection,
152             String[] selectionArgs, String sortOrder) {
153         SQLiteDatabase db = getReadableDatabase();
154         if (db == null) {
155             return null;
156         }
157         final int match = URI_MATCHER.match(uri);
158         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
159         String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
160         switch (match) {
161         case SNAPSHOTS_ID:
162             selection = DatabaseUtils.concatenateWhere(selection, "_id=?");
163             selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
164                     new String[] { Long.toString(ContentUris.parseId(uri)) });
165             // fall through
166         case SNAPSHOTS:
167             qb.setTables(TABLE_SNAPSHOTS);
168             break;
169 
170         default:
171             throw new UnsupportedOperationException("Unknown URL " + uri.toString());
172         }
173         Cursor cursor = qb.query(db, projection, selection, selectionArgs,
174                 null, null, sortOrder, limit);
175         cursor.setNotificationUri(getContext().getContentResolver(),
176                 AUTHORITY_URI);
177         return cursor;
178     }
179 
180     @Override
getType(Uri uri)181     public String getType(Uri uri) {
182         return null;
183     }
184 
185     @Override
insert(Uri uri, ContentValues values)186     public Uri insert(Uri uri, ContentValues values) {
187         SQLiteDatabase db = getWritableDatabase();
188         if (db == null) {
189             return null;
190         }
191         int match = URI_MATCHER.match(uri);
192         long id = -1;
193         switch (match) {
194         case SNAPSHOTS:
195             if (!values.containsKey(Snapshots.VIEWSTATE)) {
196                 values.put(Snapshots.VIEWSTATE, NULL_BLOB_HACK);
197             }
198             id = db.insert(TABLE_SNAPSHOTS, Snapshots.TITLE, values);
199             break;
200         default:
201             throw new UnsupportedOperationException("Unknown insert URI " + uri);
202         }
203         if (id < 0) {
204             return null;
205         }
206         Uri inserted = ContentUris.withAppendedId(uri, id);
207         getContext().getContentResolver().notifyChange(inserted, null, false);
208         return inserted;
209     }
210 
211     static final String[] DELETE_PROJECTION = new String[] {
212         Snapshots.VIEWSTATE_PATH,
213     };
deleteDataFiles(SQLiteDatabase db, String selection, String[] selectionArgs)214     private void deleteDataFiles(SQLiteDatabase db, String selection,
215             String[] selectionArgs) {
216         Cursor c = db.query(TABLE_SNAPSHOTS, DELETE_PROJECTION, selection,
217                 selectionArgs, null, null, null);
218         final Context context = getContext();
219         while (c.moveToNext()) {
220             String filename = c.getString(0);
221             if (TextUtils.isEmpty(filename)) {
222                 continue;
223             }
224             File f = context.getFileStreamPath(filename);
225             if (f.exists()) {
226                 if (!f.delete()) {
227                     f.deleteOnExit();
228                 }
229             }
230         }
231         c.close();
232     }
233 
234     @Override
delete(Uri uri, String selection, String[] selectionArgs)235     public int delete(Uri uri, String selection, String[] selectionArgs) {
236         SQLiteDatabase db = getWritableDatabase();
237         if (db == null) {
238             return 0;
239         }
240         int match = URI_MATCHER.match(uri);
241         int deleted = 0;
242         switch (match) {
243         case SNAPSHOTS_ID: {
244             selection = DatabaseUtils.concatenateWhere(selection, TABLE_SNAPSHOTS + "._id=?");
245             selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
246                     new String[] { Long.toString(ContentUris.parseId(uri)) });
247             // fall through
248         }
249         case SNAPSHOTS:
250             deleteDataFiles(db, selection, selectionArgs);
251             deleted = db.delete(TABLE_SNAPSHOTS, selection, selectionArgs);
252             break;
253         default:
254             throw new UnsupportedOperationException("Unknown delete URI " + uri);
255         }
256         if (deleted > 0) {
257             getContext().getContentResolver().notifyChange(uri, null, false);
258         }
259         return deleted;
260     }
261 
262     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)263     public int update(Uri uri, ContentValues values, String selection,
264             String[] selectionArgs) {
265         throw new UnsupportedOperationException("not implemented");
266     }
267 
268 }
269