diff options
Diffstat (limited to 'src/com/android/providers/downloads/DownloadProvider.java')
-rw-r--r-- | src/com/android/providers/downloads/DownloadProvider.java | 731 |
1 files changed, 0 insertions, 731 deletions
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java deleted file mode 100644 index f7cdd51e..00000000 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ /dev/null @@ -1,731 +0,0 @@ -/* - * Copyright (C) 2007 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.providers.downloads; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.UriMatcher; -import android.content.pm.PackageManager; -import android.database.CrossProcessCursor; -import android.database.Cursor; -import android.database.CursorWindow; -import android.database.CursorWrapper; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; -import android.database.SQLException; -import android.net.Uri; -import android.os.Binder; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.provider.Downloads; -import android.util.Config; -import android.util.Log; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.HashSet; - - -/** - * Allows application to interact with the download manager. - */ -public final class DownloadProvider extends ContentProvider { - - /** Database filename */ - private static final String DB_NAME = "downloads.db"; - /** Current database version */ - private static final int DB_VERSION = 100; - /** Database version from which upgrading is a nop */ - private static final int DB_VERSION_NOP_UPGRADE_FROM = 31; - /** Database version to which upgrading is a nop */ - private static final int DB_VERSION_NOP_UPGRADE_TO = 100; - /** Name of table in the database */ - private static final String DB_TABLE = "downloads"; - - /** MIME type for the entire download list */ - private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download"; - /** MIME type for an individual download */ - private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download"; - - /** URI matcher used to recognize URIs sent by applications */ - private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); - /** URI matcher constant for the URI of the entire download list */ - private static final int DOWNLOADS = 1; - /** URI matcher constant for the URI of an individual download */ - private static final int DOWNLOADS_ID = 2; - static { - sURIMatcher.addURI("downloads", "download", DOWNLOADS); - sURIMatcher.addURI("downloads", "download/#", DOWNLOADS_ID); - } - - private static final String[] sAppReadableColumnsArray = new String[] { - Downloads._ID, - Downloads.APP_DATA, - Downloads._DATA, - Downloads.MIMETYPE, - Downloads.VISIBILITY, - Downloads.CONTROL, - Downloads.STATUS, - Downloads.LAST_MODIFICATION, - Downloads.NOTIFICATION_PACKAGE, - Downloads.NOTIFICATION_CLASS, - Downloads.TOTAL_BYTES, - Downloads.CURRENT_BYTES, - Downloads.TITLE, - Downloads.DESCRIPTION - }; - - private static HashSet<String> sAppReadableColumnsSet; - static { - sAppReadableColumnsSet = new HashSet<String>(); - for (int i = 0; i < sAppReadableColumnsArray.length; ++i) { - sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]); - } - } - - /** The database that lies underneath this content provider */ - private SQLiteOpenHelper mOpenHelper = null; - - /** - * Creates and updated database on demand when opening it. - * Helper class to create database the first time the provider is - * initialized and upgrade it when a new version of the provider needs - * an updated version of the database. - */ - private final class DatabaseHelper extends SQLiteOpenHelper { - - public DatabaseHelper(final Context context) { - super(context, DB_NAME, null, DB_VERSION); - } - - /** - * Creates database the first time we try to open it. - */ - @Override - public void onCreate(final SQLiteDatabase db) { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "populating new database"); - } - createTable(db); - } - - /* (not a javadoc comment) - * Checks data integrity when opening the database. - */ - /* - * @Override - * public void onOpen(final SQLiteDatabase db) { - * super.onOpen(db); - * } - */ - - /** - * Updates the database format when a content provider is used - * with a database that was created with a different format. - */ - // Note: technically, this could also be a downgrade, so if we want - // to gracefully handle upgrades we should be careful about - // what to do on downgrades. - @Override - public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) { - if (oldV == DB_VERSION_NOP_UPGRADE_FROM) { - if (newV == DB_VERSION_NOP_UPGRADE_TO) { // that's a no-op upgrade. - return; - } - // NOP_FROM and NOP_TO are identical, just in different codelines. Upgrading - // from NOP_FROM is the same as upgrading from NOP_TO. - oldV = DB_VERSION_NOP_UPGRADE_TO; - } - Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV + " to " + newV - + ", which will destroy all old data"); - dropTable(db); - createTable(db); - } - } - - /** - * Initializes the content provider when it is created. - */ - @Override - public boolean onCreate() { - mOpenHelper = new DatabaseHelper(getContext()); - return true; - } - - /** - * Returns the content-provider-style MIME types of the various - * types accessible through this content provider. - */ - @Override - public String getType(final Uri uri) { - int match = sURIMatcher.match(uri); - switch (match) { - case DOWNLOADS: { - return DOWNLOAD_LIST_TYPE; - } - case DOWNLOADS_ID: { - return DOWNLOAD_TYPE; - } - default: { - if (Constants.LOGV) { - Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri); - } - throw new IllegalArgumentException("Unknown URI: " + uri); - } - } - } - - /** - * Creates the table that'll hold the download information. - */ - private void createTable(SQLiteDatabase db) { - try { - db.execSQL("CREATE TABLE " + DB_TABLE + "(" + - Downloads._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Downloads.URI + " TEXT, " + - Constants.RETRY_AFTER___REDIRECT_COUNT + " INTEGER, " + - Downloads.APP_DATA + " TEXT, " + - Downloads.NO_INTEGRITY + " BOOLEAN, " + - Downloads.FILENAME_HINT + " TEXT, " + - Constants.OTA_UPDATE + " BOOLEAN, " + - Downloads._DATA + " TEXT, " + - Downloads.MIMETYPE + " TEXT, " + - Downloads.DESTINATION + " INTEGER, " + - Constants.NO_SYSTEM_FILES + " BOOLEAN, " + - Downloads.VISIBILITY + " INTEGER, " + - Downloads.CONTROL + " INTEGER, " + - Downloads.STATUS + " INTEGER, " + - Constants.FAILED_CONNECTIONS + " INTEGER, " + - Downloads.LAST_MODIFICATION + " BIGINT, " + - Downloads.NOTIFICATION_PACKAGE + " TEXT, " + - Downloads.NOTIFICATION_CLASS + " TEXT, " + - Downloads.NOTIFICATION_EXTRAS + " TEXT, " + - Downloads.COOKIE_DATA + " TEXT, " + - Downloads.USER_AGENT + " TEXT, " + - Downloads.REFERER + " TEXT, " + - Downloads.TOTAL_BYTES + " INTEGER, " + - Downloads.CURRENT_BYTES + " INTEGER, " + - Constants.ETAG + " TEXT, " + - Constants.UID + " INTEGER, " + - Downloads.OTHER_UID + " INTEGER, " + - Downloads.TITLE + " TEXT, " + - Downloads.DESCRIPTION + " TEXT, " + - Constants.MEDIA_SCANNED + " BOOLEAN);"); - } catch (SQLException ex) { - Log.e(Constants.TAG, "couldn't create table in downloads database"); - throw ex; - } - } - - /** - * Deletes the table that holds the download information. - */ - private void dropTable(SQLiteDatabase db) { - try { - db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); - } catch (SQLException ex) { - Log.e(Constants.TAG, "couldn't drop table in downloads database"); - throw ex; - } - } - - /** - * Inserts a row in the database - */ - @Override - public Uri insert(final Uri uri, final ContentValues values) { - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - - if (sURIMatcher.match(uri) != DOWNLOADS) { - if (Config.LOGD) { - Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri); - } - throw new IllegalArgumentException("Unknown/Invalid URI " + uri); - } - - ContentValues filteredValues = new ContentValues(); - - copyString(Downloads.URI, values, filteredValues); - copyString(Downloads.APP_DATA, values, filteredValues); - copyBoolean(Downloads.NO_INTEGRITY, values, filteredValues); - copyString(Downloads.FILENAME_HINT, values, filteredValues); - copyString(Downloads.MIMETYPE, values, filteredValues); - Integer dest = values.getAsInteger(Downloads.DESTINATION); - if (dest != null) { - if (getContext().checkCallingPermission(Downloads.PERMISSION_ACCESS_ADVANCED) - != PackageManager.PERMISSION_GRANTED - && dest != Downloads.DESTINATION_EXTERNAL - && dest != Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE) { - throw new SecurityException("unauthorized destination code"); - } - filteredValues.put(Downloads.DESTINATION, dest); - } - Integer vis = values.getAsInteger(Downloads.VISIBILITY); - if (vis == null) { - if (dest == Downloads.DESTINATION_EXTERNAL) { - filteredValues.put(Downloads.VISIBILITY, - Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - } else { - filteredValues.put(Downloads.VISIBILITY, Downloads.VISIBILITY_HIDDEN); - } - } else { - filteredValues.put(Downloads.VISIBILITY, vis); - } - copyInteger(Downloads.CONTROL, values, filteredValues); - filteredValues.put(Downloads.STATUS, Downloads.STATUS_PENDING); - filteredValues.put(Downloads.LAST_MODIFICATION, System.currentTimeMillis()); - String pckg = values.getAsString(Downloads.NOTIFICATION_PACKAGE); - String clazz = values.getAsString(Downloads.NOTIFICATION_CLASS); - if (pckg != null && clazz != null) { - int uid = Binder.getCallingUid(); - try { - if (uid == 0 || - getContext().getPackageManager().getApplicationInfo(pckg, 0).uid == uid) { - filteredValues.put(Downloads.NOTIFICATION_PACKAGE, pckg); - filteredValues.put(Downloads.NOTIFICATION_CLASS, clazz); - } - } catch (PackageManager.NameNotFoundException ex) { - /* ignored for now */ - } - } - copyString(Downloads.NOTIFICATION_EXTRAS, values, filteredValues); - copyString(Downloads.COOKIE_DATA, values, filteredValues); - copyString(Downloads.USER_AGENT, values, filteredValues); - copyString(Downloads.REFERER, values, filteredValues); - if (getContext().checkCallingPermission(Downloads.PERMISSION_ACCESS_ADVANCED) - == PackageManager.PERMISSION_GRANTED) { - copyInteger(Downloads.OTHER_UID, values, filteredValues); - } - filteredValues.put(Constants.UID, Binder.getCallingUid()); - if (Binder.getCallingUid() == 0) { - copyInteger(Constants.UID, values, filteredValues); - } - copyString(Downloads.TITLE, values, filteredValues); - copyString(Downloads.DESCRIPTION, values, filteredValues); - - if (Constants.LOGVV) { - Log.v(Constants.TAG, "initiating download with UID " - + filteredValues.getAsInteger(Constants.UID)); - if (filteredValues.containsKey(Downloads.OTHER_UID)) { - Log.v(Constants.TAG, "other UID " + - filteredValues.getAsInteger(Downloads.OTHER_UID)); - } - } - - Context context = getContext(); - context.startService(new Intent(context, DownloadService.class)); - - long rowID = db.insert(DB_TABLE, null, filteredValues); - - Uri ret = null; - - if (rowID != -1) { - context.startService(new Intent(context, DownloadService.class)); - ret = Uri.parse(Downloads.CONTENT_URI + "/" + rowID); - context.getContentResolver().notifyChange(uri, null); - } else { - if (Config.LOGD) { - Log.d(Constants.TAG, "couldn't insert into downloads database"); - } - } - - return ret; - } - - /** - * Starts a database query - */ - @Override - public Cursor query(final Uri uri, String[] projection, - final String selection, final String[] selectionArgs, - final String sort) { - - Helpers.validateSelection(selection, sAppReadableColumnsSet); - - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - - int match = sURIMatcher.match(uri); - boolean emptyWhere = true; - switch (match) { - case DOWNLOADS: { - qb.setTables(DB_TABLE); - break; - } - case DOWNLOADS_ID: { - qb.setTables(DB_TABLE); - qb.appendWhere(Downloads._ID + "="); - qb.appendWhere(uri.getPathSegments().get(1)); - emptyWhere = false; - break; - } - default: { - if (Constants.LOGV) { - Log.v(Constants.TAG, "querying unknown URI: " + uri); - } - throw new IllegalArgumentException("Unknown URI: " + uri); - } - } - - if (Binder.getCallingPid() != Process.myPid() && Binder.getCallingUid() != 0) { - if (!emptyWhere) { - qb.appendWhere(" AND "); - } - qb.appendWhere("( " + Constants.UID + "=" + Binder.getCallingUid() + " OR " - + Downloads.OTHER_UID + "=" + Binder.getCallingUid() + " )"); - emptyWhere = false; - - if (projection == null) { - projection = sAppReadableColumnsArray; - } else { - for (int i = 0; i < projection.length; ++i) { - if (!sAppReadableColumnsSet.contains(projection[i])) { - throw new IllegalArgumentException( - "column " + projection[i] + " is not allowed in queries"); - } - } - } - } - - if (Constants.LOGVV) { - java.lang.StringBuilder sb = new java.lang.StringBuilder(); - sb.append("starting query, database is "); - if (db != null) { - sb.append("not "); - } - sb.append("null; "); - if (projection == null) { - sb.append("projection is null; "); - } else if (projection.length == 0) { - sb.append("projection is empty; "); - } else { - for (int i = 0; i < projection.length; ++i) { - sb.append("projection["); - sb.append(i); - sb.append("] is "); - sb.append(projection[i]); - sb.append("; "); - } - } - sb.append("selection is "); - sb.append(selection); - sb.append("; "); - if (selectionArgs == null) { - sb.append("selectionArgs is null; "); - } else if (selectionArgs.length == 0) { - sb.append("selectionArgs is empty; "); - } else { - for (int i = 0; i < selectionArgs.length; ++i) { - sb.append("selectionArgs["); - sb.append(i); - sb.append("] is "); - sb.append(selectionArgs[i]); - sb.append("; "); - } - } - sb.append("sort is "); - sb.append(sort); - sb.append("."); - Log.v(Constants.TAG, sb.toString()); - } - - Cursor ret = qb.query(db, projection, selection, selectionArgs, - null, null, sort); - - if (ret != null) { - ret = new ReadOnlyCursorWrapper(ret); - } - - if (ret != null) { - ret.setNotificationUri(getContext().getContentResolver(), uri); - if (Constants.LOGVV) { - Log.v(Constants.TAG, - "created cursor " + ret + " on behalf of " + Binder.getCallingPid()); - } - } else { - if (Constants.LOGV) { - Log.v(Constants.TAG, "query failed in downloads database"); - } - } - - return ret; - } - - /** - * Updates a row in the database - */ - @Override - public int update(final Uri uri, final ContentValues values, - final String where, final String[] whereArgs) { - - Helpers.validateSelection(where, sAppReadableColumnsSet); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - - int count; - long rowId = 0; - boolean startService = false; - - ContentValues filteredValues; - if (Binder.getCallingPid() != Process.myPid()) { - filteredValues = new ContentValues(); - copyString(Downloads.APP_DATA, values, filteredValues); - copyInteger(Downloads.VISIBILITY, values, filteredValues); - Integer i = values.getAsInteger(Downloads.CONTROL); - if (i != null) { - filteredValues.put(Downloads.CONTROL, i); - startService = true; - } - copyInteger(Downloads.CONTROL, values, filteredValues); - copyString(Downloads.TITLE, values, filteredValues); - copyString(Downloads.DESCRIPTION, values, filteredValues); - } else { - filteredValues = values; - } - int match = sURIMatcher.match(uri); - switch (match) { - case DOWNLOADS: - case DOWNLOADS_ID: { - String myWhere; - if (where != null) { - if (match == DOWNLOADS) { - myWhere = "( " + where + " )"; - } else { - myWhere = "( " + where + " ) AND "; - } - } else { - myWhere = ""; - } - if (match == DOWNLOADS_ID) { - String segment = uri.getPathSegments().get(1); - rowId = Long.parseLong(segment); - myWhere += " ( " + Downloads._ID + " = " + rowId + " ) "; - } - if (Binder.getCallingPid() != Process.myPid() && Binder.getCallingUid() != 0) { - myWhere += " AND ( " + Constants.UID + "=" + Binder.getCallingUid() + " OR " - + Downloads.OTHER_UID + "=" + Binder.getCallingUid() + " )"; - } - if (filteredValues.size() > 0) { - count = db.update(DB_TABLE, filteredValues, myWhere, whereArgs); - } else { - count = 0; - } - break; - } - default: { - if (Config.LOGD) { - Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri); - } - throw new UnsupportedOperationException("Cannot update URI: " + uri); - } - } - getContext().getContentResolver().notifyChange(uri, null); - if (startService) { - Context context = getContext(); - context.startService(new Intent(context, DownloadService.class)); - } - return count; - } - - /** - * Deletes a row in the database - */ - @Override - public int delete(final Uri uri, final String where, - final String[] whereArgs) { - - Helpers.validateSelection(where, sAppReadableColumnsSet); - - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int count; - int match = sURIMatcher.match(uri); - switch (match) { - case DOWNLOADS: - case DOWNLOADS_ID: { - String myWhere; - if (where != null) { - if (match == DOWNLOADS) { - myWhere = "( " + where + " )"; - } else { - myWhere = "( " + where + " ) AND "; - } - } else { - myWhere = ""; - } - if (match == DOWNLOADS_ID) { - String segment = uri.getPathSegments().get(1); - long rowId = Long.parseLong(segment); - myWhere += " ( " + Downloads._ID + " = " + rowId + " ) "; - } - if (Binder.getCallingPid() != Process.myPid() && Binder.getCallingUid() != 0) { - myWhere += " AND ( " + Constants.UID + "=" + Binder.getCallingUid() + " OR " - + Downloads.OTHER_UID + "=" + Binder.getCallingUid() + " )"; - } - count = db.delete(DB_TABLE, myWhere, whereArgs); - break; - } - default: { - if (Config.LOGD) { - Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri); - } - throw new UnsupportedOperationException("Cannot delete URI: " + uri); - } - } - getContext().getContentResolver().notifyChange(uri, null); - return count; - } - - /** - * Remotely opens a file - */ - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) - throws FileNotFoundException { - if (Constants.LOGVV) { - Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode - + ", uid: " + Binder.getCallingUid()); - Cursor cursor = query(Downloads.CONTENT_URI, new String[] { "_id" }, null, null, "_id"); - if (cursor == null) { - Log.v(Constants.TAG, "null cursor in openFile"); - } else { - if (!cursor.moveToFirst()) { - Log.v(Constants.TAG, "empty cursor in openFile"); - } else { - do { - Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available"); - } while(cursor.moveToNext()); - } - cursor.close(); - } - cursor = query(uri, new String[] { "_data" }, null, null, null); - if (cursor == null) { - Log.v(Constants.TAG, "null cursor in openFile"); - } else { - if (!cursor.moveToFirst()) { - Log.v(Constants.TAG, "empty cursor in openFile"); - } else { - String filename = cursor.getString(0); - Log.v(Constants.TAG, "filename in openFile: " + filename); - if (new java.io.File(filename).isFile()) { - Log.v(Constants.TAG, "file exists in openFile"); - } - } - cursor.close(); - } - } - - // This logic is mostly copied form openFileHelper. If openFileHelper eventually - // gets split into small bits (to extract the filename and the modebits), - // this code could use the separate bits and be deeply simplified. - Cursor c = query(uri, new String[]{"_data"}, null, null, null); - int count = (c != null) ? c.getCount() : 0; - if (count != 1) { - // If there is not exactly one result, throw an appropriate exception. - if (c != null) { - c.close(); - } - if (count == 0) { - throw new FileNotFoundException("No entry for " + uri); - } - throw new FileNotFoundException("Multiple items at " + uri); - } - - c.moveToFirst(); - String path = c.getString(0); - c.close(); - if (path == null) { - throw new FileNotFoundException("No filename found."); - } - if (!Helpers.isFilenameValid(path)) { - throw new FileNotFoundException("Invalid filename."); - } - - if (!"r".equals(mode)) { - throw new FileNotFoundException("Bad mode for " + uri + ": " + mode); - } - ParcelFileDescriptor ret = ParcelFileDescriptor.open(new File(path), - ParcelFileDescriptor.MODE_READ_ONLY); - - if (ret == null) { - if (Constants.LOGV) { - Log.v(Constants.TAG, "couldn't open file"); - } - throw new FileNotFoundException("couldn't open file"); - } else { - ContentValues values = new ContentValues(); - values.put(Downloads.LAST_MODIFICATION, System.currentTimeMillis()); - update(uri, values, null, null); - } - return ret; - } - - private static final void copyInteger(String key, ContentValues from, ContentValues to) { - Integer i = from.getAsInteger(key); - if (i != null) { - to.put(key, i); - } - } - - private static final void copyBoolean(String key, ContentValues from, ContentValues to) { - Boolean b = from.getAsBoolean(key); - if (b != null) { - to.put(key, b); - } - } - - private static final void copyString(String key, ContentValues from, ContentValues to) { - String s = from.getAsString(key); - if (s != null) { - to.put(key, s); - } - } - - private class ReadOnlyCursorWrapper extends CursorWrapper implements CrossProcessCursor { - public ReadOnlyCursorWrapper(Cursor cursor) { - super(cursor); - mCursor = (CrossProcessCursor) cursor; - } - - public boolean deleteRow() { - throw new SecurityException("Download manager cursors are read-only"); - } - - public boolean commitUpdates() { - throw new SecurityException("Download manager cursors are read-only"); - } - - public void fillWindow(int pos, CursorWindow window) { - mCursor.fillWindow(pos, window); - } - - public CursorWindow getWindow() { - return mCursor.getWindow(); - } - - public boolean onMove(int oldPosition, int newPosition) { - return mCursor.onMove(oldPosition, newPosition); - } - - private CrossProcessCursor mCursor; - } - -} |