FileDocCategorySizeDatePackage
DownloadProvider.javaAPI DocAndroid 1.5 API27969Wed May 06 22:42:48 BST 2009com.android.providers.downloads

DownloadProvider

public final class DownloadProvider extends android.content.ContentProvider
Allows application to interact with the download manager.

Fields Summary
private static final String
DB_NAME
Database filename
private static final int
DB_VERSION
Current database version
private static final int
DB_VERSION_NOP_UPGRADE_FROM
Database version from which upgrading is a nop
private static final int
DB_VERSION_NOP_UPGRADE_TO
Database version to which upgrading is a nop
private static final String
DB_TABLE
Name of table in the database
private static final String
DOWNLOAD_LIST_TYPE
MIME type for the entire download list
private static final String
DOWNLOAD_TYPE
MIME type for an individual download
private static final android.content.UriMatcher
sURIMatcher
URI matcher used to recognize URIs sent by applications
private static final int
DOWNLOADS
URI matcher constant for the URI of the entire download list
private static final int
DOWNLOADS_ID
URI matcher constant for the URI of an individual download
private static final String[]
sAppReadableColumnsArray
private static HashSet
sAppReadableColumnsSet
private android.database.sqlite.SQLiteOpenHelper
mOpenHelper
The database that lies underneath this content provider
Constructors Summary
Methods Summary
private static final voidcopyBoolean(java.lang.String key, android.content.ContentValues from, android.content.ContentValues to)

        Boolean b = from.getAsBoolean(key);
        if (b != null) {
            to.put(key, b);
        }
    
private static final voidcopyInteger(java.lang.String key, android.content.ContentValues from, android.content.ContentValues to)

        Integer i = from.getAsInteger(key);
        if (i != null) {
            to.put(key, i);
        }
    
private static final voidcopyString(java.lang.String key, android.content.ContentValues from, android.content.ContentValues to)

        String s = from.getAsString(key);
        if (s != null) {
            to.put(key, s);
        }
    
private voidcreateTable(android.database.sqlite.SQLiteDatabase db)
Creates the table that'll hold the download information.

        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;
        }
    
public intdelete(android.net.Uri uri, java.lang.String where, java.lang.String[] whereArgs)
Deletes a row in the database


        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;
    
private voiddropTable(android.database.sqlite.SQLiteDatabase db)
Deletes the table that holds the download information.

        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;
        }
    
public java.lang.StringgetType(android.net.Uri uri)
Returns the content-provider-style MIME types of the various types accessible through this content provider.

        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);
            }
        }
    
public android.net.Uriinsert(android.net.Uri uri, android.content.ContentValues values)
Inserts a row in the database

        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;
    
public booleanonCreate()
Initializes the content provider when it is created.

        mOpenHelper = new DatabaseHelper(getContext());
        return true;
    
public android.os.ParcelFileDescriptoropenFile(android.net.Uri uri, java.lang.String mode)
Remotely opens a file

        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;
    
public android.database.Cursorquery(android.net.Uri uri, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sort)
Starts a database query


        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;
    
public intupdate(android.net.Uri uri, android.content.ContentValues values, java.lang.String where, java.lang.String[] whereArgs)
Updates a row in the database


        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;