Home | History | Annotate | Download | only in provider
      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.BroadcastReceiver;
     19 import android.content.ContentProvider;
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.content.UriMatcher;
     26 import android.database.Cursor;
     27 import android.database.DatabaseUtils;
     28 import android.database.sqlite.SQLiteDatabase;
     29 import android.database.sqlite.SQLiteOpenHelper;
     30 import android.database.sqlite.SQLiteQueryBuilder;
     31 import android.net.Uri;
     32 import android.os.Environment;
     33 import android.provider.BrowserContract;
     34 
     35 import java.io.File;
     36 
     37 /**
     38  * This provider is expected to be potentially flaky. It uses a database
     39  * stored on external storage, which could be yanked unexpectedly.
     40  */
     41 public class SnapshotProvider extends ContentProvider {
     42 
     43     public static interface Snapshots {
     44 
     45         public static final Uri CONTENT_URI = Uri.withAppendedPath(
     46                 SnapshotProvider.AUTHORITY_URI, "snapshots");
     47         public static final String _ID = "_id";
     48         public static final String VIEWSTATE = "view_state";
     49         public static final String BACKGROUND = "background";
     50         public static final String TITLE = "title";
     51         public static final String URL = "url";
     52         public static final String FAVICON = "favicon";
     53         public static final String THUMBNAIL = "thumbnail";
     54         public static final String DATE_CREATED = "date_created";
     55     }
     56 
     57     public static final String AUTHORITY = "com.android.browser.snapshots";
     58     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
     59 
     60     static final String TABLE_SNAPSHOTS = "snapshots";
     61     static final int SNAPSHOTS = 10;
     62     static final int SNAPSHOTS_ID = 11;
     63     static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
     64 
     65     SnapshotDatabaseHelper mOpenHelper;
     66 
     67     static {
     68         URI_MATCHER.addURI(AUTHORITY, "snapshots", SNAPSHOTS);
     69         URI_MATCHER.addURI(AUTHORITY, "snapshots/#", SNAPSHOTS_ID);
     70     }
     71 
     72     final static class SnapshotDatabaseHelper extends SQLiteOpenHelper {
     73 
     74         static final String DATABASE_NAME = "snapshots.db";
     75         static final int DATABASE_VERSION = 2;
     76 
     77         public SnapshotDatabaseHelper(Context context) {
     78             super(context, getFullDatabaseName(context), null, DATABASE_VERSION);
     79         }
     80 
     81         static String getFullDatabaseName(Context context) {
     82             File dir = context.getExternalFilesDir(null);
     83             return new File(dir, DATABASE_NAME).getAbsolutePath();
     84         }
     85 
     86         @Override
     87         public void onCreate(SQLiteDatabase db) {
     88             db.execSQL("CREATE TABLE " + TABLE_SNAPSHOTS + "(" +
     89                     Snapshots._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
     90                     Snapshots.TITLE + " TEXT," +
     91                     Snapshots.URL + " TEXT NOT NULL," +
     92                     Snapshots.DATE_CREATED + " INTEGER," +
     93                     Snapshots.FAVICON + " BLOB," +
     94                     Snapshots.THUMBNAIL + " BLOB," +
     95                     Snapshots.BACKGROUND + " INTEGER," +
     96                     Snapshots.VIEWSTATE + " BLOB NOT NULL" +
     97                     ");");
     98         }
     99 
    100         @Override
    101         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    102             if (oldVersion < 2) {
    103                 db.execSQL("DROP TABLE " + TABLE_SNAPSHOTS);
    104                 onCreate(db);
    105             }
    106         }
    107 
    108     }
    109 
    110     @Override
    111     public boolean onCreate() {
    112         IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
    113         filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
    114         getContext().registerReceiver(mExternalStorageReceiver, filter);
    115         return true;
    116     }
    117 
    118     final BroadcastReceiver mExternalStorageReceiver = new BroadcastReceiver() {
    119 
    120         @Override
    121         public void onReceive(Context context, Intent intent) {
    122             if (mOpenHelper != null) {
    123                 try {
    124                     mOpenHelper.close();
    125                 } catch (Throwable t) {
    126                     // We failed to close the open helper, which most likely means
    127                     // another thread is busy attempting to open the database
    128                     // or use the database. Let that thread try to gracefully
    129                     // deal with the error
    130                 }
    131             }
    132         }
    133     };
    134 
    135     SQLiteDatabase getWritableDatabase() {
    136         String state = Environment.getExternalStorageState();
    137         if (Environment.MEDIA_MOUNTED.equals(state)) {
    138             try {
    139                 if (mOpenHelper == null) {
    140                     mOpenHelper = new SnapshotDatabaseHelper(getContext());
    141                 }
    142                 return mOpenHelper.getWritableDatabase();
    143             } catch (Throwable t) {
    144                 return null;
    145             }
    146         }
    147         return null;
    148     }
    149 
    150     SQLiteDatabase getReadableDatabase() {
    151         String state = Environment.getExternalStorageState();
    152         if (Environment.MEDIA_MOUNTED.equals(state)
    153                 || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
    154             try {
    155                 if (mOpenHelper == null) {
    156                     mOpenHelper = new SnapshotDatabaseHelper(getContext());
    157                 }
    158                 return mOpenHelper.getReadableDatabase();
    159             } catch (Throwable t) {
    160                 return null;
    161             }
    162         }
    163         return null;
    164     }
    165 
    166     @Override
    167     public Cursor query(Uri uri, String[] projection, String selection,
    168             String[] selectionArgs, String sortOrder) {
    169         SQLiteDatabase db = getReadableDatabase();
    170         if (db == null) {
    171             return null;
    172         }
    173         final int match = URI_MATCHER.match(uri);
    174         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    175         String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
    176         switch (match) {
    177         case SNAPSHOTS_ID:
    178             selection = DatabaseUtils.concatenateWhere(selection, "_id=?");
    179             selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
    180                     new String[] { Long.toString(ContentUris.parseId(uri)) });
    181             // fall through
    182         case SNAPSHOTS:
    183             qb.setTables(TABLE_SNAPSHOTS);
    184             break;
    185 
    186         default:
    187             throw new UnsupportedOperationException("Unknown URL " + uri.toString());
    188         }
    189         try {
    190             Cursor cursor = qb.query(db, projection, selection, selectionArgs,
    191                     null, null, sortOrder, limit);
    192             cursor.setNotificationUri(getContext().getContentResolver(),
    193                     AUTHORITY_URI);
    194             return cursor;
    195         } catch (Throwable t) {
    196             return null;
    197         }
    198     }
    199 
    200     @Override
    201     public String getType(Uri uri) {
    202         return null;
    203     }
    204 
    205     @Override
    206     public Uri insert(Uri uri, ContentValues values) {
    207         SQLiteDatabase db = getWritableDatabase();
    208         if (db == null) {
    209             return null;
    210         }
    211         int match = URI_MATCHER.match(uri);
    212         long id = -1;
    213         switch (match) {
    214         case SNAPSHOTS:
    215             try {
    216                 id = db.insert(TABLE_SNAPSHOTS, Snapshots.TITLE, values);
    217             } catch (Throwable t) {
    218                 id = -1;
    219             }
    220             break;
    221         default:
    222             throw new UnsupportedOperationException("Unknown insert URI " + uri);
    223         }
    224         if (id < 0) {
    225             return null;
    226         }
    227         Uri inserted = ContentUris.withAppendedId(uri, id);
    228         getContext().getContentResolver().notifyChange(inserted, null, false);
    229         return inserted;
    230     }
    231 
    232     @Override
    233     public int delete(Uri uri, String selection, String[] selectionArgs) {
    234         SQLiteDatabase db = getWritableDatabase();
    235         if (db == null) {
    236             return 0;
    237         }
    238         int match = URI_MATCHER.match(uri);
    239         int deleted = 0;
    240         switch (match) {
    241         case SNAPSHOTS_ID: {
    242             selection = DatabaseUtils.concatenateWhere(selection, TABLE_SNAPSHOTS + "._id=?");
    243             selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
    244                     new String[] { Long.toString(ContentUris.parseId(uri)) });
    245             // fall through
    246         }
    247         case SNAPSHOTS:
    248             try {
    249                 deleted = db.delete(TABLE_SNAPSHOTS, selection, selectionArgs);
    250             } catch (Throwable t) {
    251             }
    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
    263     public int update(Uri uri, ContentValues values, String selection,
    264             String[] selectionArgs) {
    265         throw new UnsupportedOperationException("not implemented");
    266     }
    267 
    268 }
    269