DownloadProvider.java revision 4bebe75b3e2361d7fb0aa966598c41c45ad9317f
157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/*
257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * you may not use this file except in compliance with the License.
657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * You may obtain a copy of the License at
757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
1057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * See the License for the specific language governing permissions and
1457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * limitations under the License.
1557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
1657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpackage com.android.providers.downloads;
1857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentProvider;
203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howardimport android.content.ContentUris;
2157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentValues;
2257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Context;
2357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Intent;
2457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.UriMatcher;
2591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.ApplicationInfo;
2657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.pm.PackageManager;
2791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.PackageManager.NameNotFoundException;
281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CrossProcessCursor;
2957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.Cursor;
301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CursorWindow;
311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CursorWrapper;
32c6f5aad265cfc36a64cd2bdb5adf3cc9736bbd80Jean-Baptiste Queruimport android.database.SQLException;
3357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
3457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
3557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
3657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.net.Uri;
3757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Binder;
38b06b739b078ce4b00600487cfec31659647bf31fSteve Howardimport android.os.Environment;
3957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.ParcelFileDescriptor;
4057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Process;
4157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.provider.Downloads;
4257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.util.Log;
4357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport com.google.common.annotations.VisibleForTesting;
455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.io.File;
4757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport java.io.FileNotFoundException;
481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.util.HashSet;
49e61798da80558450f580ed948d0d469bd6423d8eSteve Howardimport java.util.Iterator;
505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport java.util.Map;
511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
5257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/**
5457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Allows application to interact with the download manager.
5557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
5657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpublic final class DownloadProvider extends ContentProvider {
5757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Database filename */
5857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_NAME = "downloads.db";
591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    /** Current database version */
6071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard    private static final int DB_VERSION = 103;
6157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Name of table in the database */
6257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_TABLE = "downloads";
6357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for the entire download list */
6557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download";
6657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for an individual download */
6757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download";
6857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher used to recognize URIs sent by applications */
7057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads belonging to the calling UID */
723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS = 1;
733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of an individual download belonging to the calling UID */
743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS_ID = 2;
753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads in the system */
763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS = 3;
7757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher constant for the URI of an individual download */
783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS_ID = 4;
795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /** URI matcher constant for the URI of a download's request headers */
803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int REQUEST_HEADERS_URI = 5;
8157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    static {
823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS);
853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
924bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        // temporary, for backwards compatibility
934bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS);
944bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID);
954bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads",
964bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
974bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                REQUEST_HEADERS_URI);
9857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
9957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** Different base URIs that could be used to access an individual download */
1013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final Uri[] BASE_URIS = new Uri[] {
1023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.CONTENT_URI,
1033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
1043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    };
1053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1061fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final String[] sAppReadableColumnsArray = new String[] {
1077dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._ID,
1087dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_APP_DATA,
1097dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._DATA,
1107dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_MIME_TYPE,
1117dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_VISIBILITY,
1127dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_DESTINATION,
1137dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CONTROL,
1147dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_STATUS,
1157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_LAST_MODIFICATION,
1167dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
1177dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
1187dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TOTAL_BYTES,
1197dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CURRENT_BYTES,
1207dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TITLE,
1210a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        Downloads.Impl.COLUMN_DESCRIPTION,
1220d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard        Downloads.Impl.COLUMN_URI,
12371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
1241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    };
1251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static HashSet<String> sAppReadableColumnsSet;
1271fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    static {
1281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        sAppReadableColumnsSet = new HashSet<String>();
1291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        for (int i = 0; i < sAppReadableColumnsArray.length; ++i) {
1301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]);
1311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
1321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
1331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** The database that lies underneath this content provider */
13557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private SQLiteOpenHelper mOpenHelper = null;
13657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
13791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    /** List of uids that can access the downloads */
13891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mSystemUid = -1;
13991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mDefContainerUid = -1;
14091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu
1416d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    @VisibleForTesting
1426d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    SystemFacade mSystemFacade;
1436d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
14457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
14557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Creates and updated database on demand when opening it.
14657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Helper class to create database the first time the provider is
14757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * initialized and upgrade it when a new version of the provider needs
14857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * an updated version of the database.
14957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
15057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private final class DatabaseHelper extends SQLiteOpenHelper {
15157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public DatabaseHelper(final Context context) {
15257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            super(context, DB_NAME, null, DB_VERSION);
15357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
15457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
15557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
15657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Creates database the first time we try to open it.
15757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
15857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
15957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
16057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
16157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                Log.v(Constants.TAG, "populating new database");
16257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
1635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            onUpgrade(db, 0, DB_VERSION);
16457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
16557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
16657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
16757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Updates the database format when a content provider is used
16857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * with a database that was created with a different format.
1695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         *
1705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Note: to support downgrades, creating a table should always drop it first if it already
1715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * exists.
17257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
17357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
1741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) {
1755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (oldV == 31) {
1765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // 31 and 100 are identical, just in different codelines. Upgrading from 31 is the
1775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // same as upgrading from 100.
1785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 100;
1795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV < 100) {
1805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // no logic to upgrade from these older version, just recreate the DB
1815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV
1825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " to version " + newV + ", which will destroy all old data");
1835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
1845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV > newV) {
1855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // user must have downgraded software; we have no way to know how to downgrade the
1865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // DB, so just recreate it
1875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.i(Constants.TAG, "Downgrading downloads database from version " + oldV
1885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " (current version is " + newV + "), destroying all old data");
1895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
1905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
1915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
1925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (int version = oldV + 1; version <= newV; version++) {
1935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                upgradeTo(db, version);
1945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
1955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
1965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
1975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
1985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Upgrade database from (version - 1) to version.
1995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
2005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void upgradeTo(SQLiteDatabase db, int version) {
2015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            switch (version) {
2025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 100:
2035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createDownloadsTable(db);
2045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 101:
2075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createHeadersTable(db);
2085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2100a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                case 102:
2110a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_PUBLIC_API,
2120a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2130a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_ROAMING,
2140a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2150a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES,
2160a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2170a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    break;
2180a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
21971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                case 103:
22071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
22171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                              "INTEGER NOT NULL DEFAULT 1");
22271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    makeCacheDownloadsInvisible(db);
22371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    break;
22471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
2255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                default:
2265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalStateException("Don't know how to upgrade to " + version);
2275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
23171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         * Set all existing downloads to the cache partition to be invisible in the downloads UI.
23271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         */
23371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        private void makeCacheDownloadsInvisible(SQLiteDatabase db) {
23471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            ContentValues values = new ContentValues();
23571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false);
23671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            String cacheSelection = Downloads.Impl.COLUMN_DESTINATION
23771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    + " != " + Downloads.Impl.DESTINATION_EXTERNAL;
23871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            db.update(DB_TABLE, values, cacheSelection, null);
23971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
24071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
24171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        /**
2420a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * Add a column to a table using ALTER TABLE.
2430a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param dbTable name of the table
2440a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnName name of the column to add
2450a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnDefinition SQL for the column definition
2460a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         */
2470a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        private void addColumn(SQLiteDatabase db, String dbTable, String columnName,
2480a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                               String columnDefinition) {
2490a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            db.execSQL("ALTER TABLE " + dbTable + " ADD COLUMN " + columnName + " "
2500a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                       + columnDefinition);
2510a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
2520a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
2530a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        /**
2545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Creates the table that'll hold the download information.
2555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
2565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createDownloadsTable(SQLiteDatabase db) {
2575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            try {
2585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
2595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("CREATE TABLE " + DB_TABLE + "(" +
2605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
2615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_URI + " TEXT, " +
2625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, " +
2635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_APP_DATA + " TEXT, " +
2645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NO_INTEGRITY + " BOOLEAN, " +
2655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_FILE_NAME_HINT + " TEXT, " +
2665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.OTA_UPDATE + " BOOLEAN, " +
2675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._DATA + " TEXT, " +
2685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_MIME_TYPE + " TEXT, " +
2695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESTINATION + " INTEGER, " +
2705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.NO_SYSTEM_FILES + " BOOLEAN, " +
2715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_VISIBILITY + " INTEGER, " +
2725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CONTROL + " INTEGER, " +
2735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_STATUS + " INTEGER, " +
2745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.FAILED_CONNECTIONS + " INTEGER, " +
2755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_LAST_MODIFICATION + " BIGINT, " +
2765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + " TEXT, " +
2775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS + " TEXT, " +
2785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + " TEXT, " +
2795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_COOKIE_DATA + " TEXT, " +
2805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_USER_AGENT + " TEXT, " +
2815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_REFERER + " TEXT, " +
2825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TOTAL_BYTES + " INTEGER, " +
2835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CURRENT_BYTES + " INTEGER, " +
2845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.ETAG + " TEXT, " +
2855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.UID + " INTEGER, " +
2865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_OTHER_UID + " INTEGER, " +
2875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TITLE + " TEXT, " +
2885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESCRIPTION + " TEXT, " +
2895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.MEDIA_SCANNED + " BOOLEAN);");
2905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } catch (SQLException ex) {
2915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.e(Constants.TAG, "couldn't create table in downloads database");
2925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                throw ex;
2935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createHeadersTable(SQLiteDatabase db) {
2975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("DROP TABLE IF EXISTS " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE);
2985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("CREATE TABLE " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + "(" +
2995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       "id INTEGER PRIMARY KEY AUTOINCREMENT," +
3005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL," +
3015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL," +
3025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL" +
3035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       ");");
30457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
30557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
30657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
30757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
30857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Initializes the content provider when it is created.
30957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
31057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
31157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public boolean onCreate() {
3126d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        if (mSystemFacade == null) {
313af28400b74de05862b470412a5c92f68e99f59f8Steve Howard            mSystemFacade = new RealSystemFacade(getContext());
3146d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        }
3156d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
31657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        mOpenHelper = new DatabaseHelper(getContext());
31791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the system uid
31891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        mSystemUid = Process.SYSTEM_UID;
31991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the default container uid. Package name hardcoded
32091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // for now.
32191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        ApplicationInfo appInfo = null;
32291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        try {
32391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            appInfo = getContext().getPackageManager().
32491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu                    getApplicationInfo("com.android.defcontainer", 0);
32591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        } catch (NameNotFoundException e) {
32691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            // TODO Auto-generated catch block
32791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            e.printStackTrace();
32891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
32991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        if (appInfo != null) {
33091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            mDefContainerUid = appInfo.uid;
33191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
33257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return true;
33357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
33457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
33557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
33657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Returns the content-provider-style MIME types of the various
33757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * types accessible through this content provider.
33857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
33957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
34057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public String getType(final Uri uri) {
34157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
34257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
3433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS: {
34457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_LIST_TYPE;
34557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
3463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID: {
34757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_TYPE;
34857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
34957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            default: {
35057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                if (Constants.LOGV) {
35157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                    Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri);
35257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                }
35357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new IllegalArgumentException("Unknown URI: " + uri);
35457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
35557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
35657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
35757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
35857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
35957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Inserts a row in the database
36057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
36157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
36257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public Uri insert(final Uri uri, final ContentValues values) {
363b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        checkInsertPermissions(values);
36457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
36557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
3663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        // note we disallow inserting into ALL_DOWNLOADS
3673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        int match = sURIMatcher.match(uri);
3683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match != MY_DOWNLOADS) {
3693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri);
37057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
37157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
37257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
3731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues = new ContentValues();
3741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
3757dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_URI, values, filteredValues);
3767dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
3777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues);
3787dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues);
3797dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues);
38071aab521efba9b28779541440c797220ec98ac97Steve Howard
38171aab521efba9b28779541440c797220ec98ac97Steve Howard        copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues);
38271aab521efba9b28779541440c797220ec98ac97Steve Howard        boolean isPublicApi =
38371aab521efba9b28779541440c797220ec98ac97Steve Howard                values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE;
38471aab521efba9b28779541440c797220ec98ac97Steve Howard
3857dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION);
386ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (dest != null) {
3877dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
3881fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    != PackageManager.PERMISSION_GRANTED
3897dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    && dest != Downloads.Impl.DESTINATION_EXTERNAL
3906d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                    && dest != Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
3916d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                    && dest != Downloads.Impl.DESTINATION_FILE_URI) {
3921fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                throw new SecurityException("unauthorized destination code");
3931fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
39471aab521efba9b28779541440c797220ec98ac97Steve Howard            // for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically
39571aab521efba9b28779541440c797220ec98ac97Steve Howard            // switch to non-purgeable download
39671aab521efba9b28779541440c797220ec98ac97Steve Howard            boolean hasNonPurgeablePermission =
39771aab521efba9b28779541440c797220ec98ac97Steve Howard                    getContext().checkCallingPermission(
39871aab521efba9b28779541440c797220ec98ac97Steve Howard                            Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE)
39971aab521efba9b28779541440c797220ec98ac97Steve Howard                            == PackageManager.PERMISSION_GRANTED;
40071aab521efba9b28779541440c797220ec98ac97Steve Howard            if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
40171aab521efba9b28779541440c797220ec98ac97Steve Howard                    && hasNonPurgeablePermission) {
40271aab521efba9b28779541440c797220ec98ac97Steve Howard                dest = Downloads.Impl.DESTINATION_CACHE_PARTITION;
40371aab521efba9b28779541440c797220ec98ac97Steve Howard            }
4046d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            if (dest == Downloads.Impl.DESTINATION_FILE_URI) {
4056d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                getContext().enforcePermission(
4066d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
4076d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        Binder.getCallingPid(), Binder.getCallingUid(),
4086d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        "need WRITE_EXTERNAL_STORAGE permission to use DESTINATION_FILE_URI");
409b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                checkFileUriDestination(values);
4106d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            }
4117dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);
412ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        }
4137dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY);
414ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (vis == null) {
4157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
4167dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
4177dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
418ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru            } else {
4197dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
4207dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_HIDDEN);
4211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
422ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        } else {
4237dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis);
42457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
4257dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
4267dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
4276d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION,
4286d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                           mSystemFacade.currentTimeMillis());
4290a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
4307dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
4317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
4320a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (pckg != null && (clazz != null || isPublicApi)) {
4331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            int uid = Binder.getCallingUid();
4341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            try {
4350a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) {
4367dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg);
4370a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    if (clazz != null) {
4380a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                        filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz);
4390a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    }
4401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
4411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } catch (PackageManager.NameNotFoundException ex) {
4421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                /* ignored for now */
44357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
44457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
4457dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues);
4467dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues);
4477dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues);
4487dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues);
4497dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
4501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                == PackageManager.PERMISSION_GRANTED) {
4517dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues);
45257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
4531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        filteredValues.put(Constants.UID, Binder.getCallingUid());
4541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingUid() == 0) {
4551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            copyInteger(Constants.UID, values, filteredValues);
45657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
457a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, "");
458a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, "");
459b5629da794cb3c1ca1970d206343743b165b9644Steve Howard        filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
46057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
46171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) {
46271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues);
46371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        } else {
46471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            // by default, make external downloads visible in the UI
46571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL);
46671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal);
46771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
46871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
4690a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (isPublicApi) {
4700a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);
4710a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);
4720a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
4730a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
4741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Constants.LOGVV) {
4751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            Log.v(Constants.TAG, "initiating download with UID "
4761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    + filteredValues.getAsInteger(Constants.UID));
4777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) {
4781fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "other UID " +
4797dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID));
4801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
48157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
48257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
48357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        Context context = getContext();
48457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        context.startService(new Intent(context, DownloadService.class));
48557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
4861fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        long rowID = db.insert(DB_TABLE, null, filteredValues);
4873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (rowID == -1) {
4883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.d(Constants.TAG, "couldn't insert into downloads database");
4893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return null;
49057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
49157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
4923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        insertRequestHeaders(db, rowID, values);
4933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        context.startService(new Intent(context, DownloadService.class));
4943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
4953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
49657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
49757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
49857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
499b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Check that the file URI provided for DESTINATION_FILE_URI is valid.
500b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
501b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkFileUriDestination(ContentValues values) {
502b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String fileUri = values.getAsString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
503b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (fileUri == null) {
504b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException(
505b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    "DESTINATION_FILE_URI must include a file URI under COLUMN_FILE_NAME_HINT");
506b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
507b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Uri uri = Uri.parse(fileUri);
508b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (!uri.getScheme().equals("file")) {
509b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException("Not a file URI: " + uri);
510b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
511b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        File path = new File(uri.getSchemeSpecificPart());
512b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath();
513b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (!path.getPath().startsWith(externalPath)) {
514b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException("Destination must be on external storage: " + uri);
515b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
516b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
517b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
518b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
519b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to
520b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * constraints in the rest of the code. Apps without that may still access this provider through
521b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * the public API, but additional restrictions are imposed. We check those restrictions here.
522b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     *
523b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @param values ContentValues provided to insert()
524b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @throws SecurityException if the caller has insufficient permissions
525b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
526b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkInsertPermissions(ContentValues values) {
527b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS)
528b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                == PackageManager.PERMISSION_GRANTED) {
529b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            return;
530b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
531b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
532b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
533b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                "INTERNET permission is required to use the download manager");
534b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
535b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // ensure the request fits within the bounds of a public API request
536b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // first copy so we can remove values
537b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values = new ContentValues(values);
538b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
539b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // check columns whose values are restricted
540b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_IS_PUBLIC_API, Boolean.TRUE);
541b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION,
542b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE,
543b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_FILE_URI);
5449da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard
5459da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION)
5469da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                == PackageManager.PERMISSION_GRANTED) {
5479da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
5489da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                    Downloads.Impl.VISIBILITY_HIDDEN, Downloads.Impl.VISIBILITY_VISIBLE);
5499da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        } else {
5509da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
5519da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                    Downloads.Impl.VISIBILITY_VISIBLE);
5529da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        }
553b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
554b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // remove the rest of the columns that are allowed (with any value)
555b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_URI);
556b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_TITLE);
557b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_DESCRIPTION);
558b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_MIME_TYPE);
559b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_FILE_NAME_HINT); // checked later in insert()
560b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert()
561b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
562b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
56371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
564e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator();
565e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        while (iterator.hasNext()) {
566e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            String key = iterator.next().getKey();
567e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
568e61798da80558450f580ed948d0d469bd6423d8eSteve Howard                iterator.remove();
569e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            }
570e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        }
571b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
572b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // any extra columns are extraneous and disallowed
573b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (values.size() > 0) {
574b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            StringBuilder error = new StringBuilder("Invalid columns in request: ");
575b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            boolean first = true;
576b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            for (Map.Entry<String, Object> entry : values.valueSet()) {
577b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                if (!first) {
578b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    error.append(", ");
579b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                }
580b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                error.append(entry.getKey());
581b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
582b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException(error.toString());
583b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
584b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
585b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
586b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
587b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Remove column from values, and throw a SecurityException if the value isn't within the
588b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * specified allowedValues.
589b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
590b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void enforceAllowedValues(ContentValues values, String column,
591b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            Object... allowedValues) {
592b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Object value = values.get(column);
593b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(column);
594b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        for (Object allowedValue : allowedValues) {
595b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value == null && allowedValue == null) {
596b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
597b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
598b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value != null && value.equals(allowedValue)) {
599b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
600b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
601b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
602b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        throw new SecurityException("Invalid value for " + column + ": " + value);
603b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
604b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
605b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
60657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Starts a database query
60757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
60857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
6091fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    public Cursor query(final Uri uri, String[] projection,
61057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String selection, final String[] selectionArgs,
61157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String sort) {
6121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
6131fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(selection, sAppReadableColumnsSet);
6141fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
61557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
61657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
61757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
6183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        qb.setTables(DB_TABLE);
61957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
62057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
6213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == -1) {
6223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (Constants.LOGV) {
6233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "querying unknown URI: " + uri);
62457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
6253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            throw new IllegalArgumentException("Unknown URI: " + uri);
6263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
6273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
6283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == REQUEST_HEADERS_URI) {
6293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (projection != null || selection != null || sort != null) {
6303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new UnsupportedOperationException("Request header queries do not support "
6313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                                                        + "projections, selections or sorting");
63257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
6333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return queryRequestHeaders(db, uri);
6343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
6353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
6363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        String where = getWhereClause(uri, null, match);
6373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (!where.isEmpty()) {
6383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            qb.appendWhere(where);
63957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
64057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        if (shouldRestrictVisibility()) {
6421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (projection == null) {
6431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                projection = sAppReadableColumnsArray;
6441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } else {
6451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                for (int i = 0; i < projection.length; ++i) {
6461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    if (!sAppReadableColumnsSet.contains(projection[i])) {
6471fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                        throw new IllegalArgumentException(
6481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                                "column " + projection[i] + " is not allowed in queries");
6491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    }
6501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
6511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
65257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
65357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
65457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
6553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseQueryInfo(projection, selection, selectionArgs, sort, db);
65657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
65757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
65857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        Cursor ret = qb.query(db, projection, selection, selectionArgs,
65957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                              null, null, sort);
66057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
66157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret != null) {
6621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project           ret = new ReadOnlyCursorWrapper(ret);
6631fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
6641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
6651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (ret != null) {
66657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            ret.setNotificationUri(getContext().getContentResolver(), uri);
66757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
66857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                Log.v(Constants.TAG,
66957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                        "created cursor " + ret + " on behalf of " + Binder.getCallingPid());
67057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
67157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        } else {
67257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGV) {
6731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "query failed in downloads database");
67457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
67557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
67657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
67757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
67857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
67957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseQueryInfo(String[] projection, final String selection,
6813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            final String[] selectionArgs, final String sort, SQLiteDatabase db) {
6823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        java.lang.StringBuilder sb = new java.lang.StringBuilder();
6833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("starting query, database is ");
6843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (db != null) {
6853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("not ");
6863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
6873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("null; ");
6883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (projection == null) {
6893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is null; ");
6903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (projection.length == 0) {
6913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is empty; ");
6923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
6933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < projection.length; ++i) {
6943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("projection[");
6953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
6963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
6973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(projection[i]);
6983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
6993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
7003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("selection is ");
7023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(selection);
7033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("; ");
7043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (selectionArgs == null) {
7053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is null; ");
7063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (selectionArgs.length == 0) {
7073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is empty; ");
7083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
7093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < selectionArgs.length; ++i) {
7103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("selectionArgs[");
7113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
7123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
7133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(selectionArgs[i]);
7143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
7153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
7163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("sort is ");
7183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(sort);
7193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(".");
7203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Log.v(Constants.TAG, sb.toString());
7213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
7223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
7235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private String getDownloadIdFromUri(final Uri uri) {
7245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return uri.getPathSegments().get(1);
7255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
7265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
7275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
7285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Insert request headers for a download into the DB.
7295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
7305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void insertRequestHeaders(SQLiteDatabase db, long downloadId, ContentValues values) {
7315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        ContentValues rowValues = new ContentValues();
7325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID, downloadId);
7335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        for (Map.Entry<String, Object> entry : values.valueSet()) {
7345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            String key = entry.getKey();
7355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
7365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String headerLine = entry.getValue().toString();
7375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                if (!headerLine.contains(":")) {
7385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalArgumentException("Invalid HTTP header line: " + headerLine);
7395224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                }
7405224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String[] parts = headerLine.split(":", 2);
7415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_HEADER, parts[0].trim());
7425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_VALUE, parts[1].trim());
7435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.insert(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, null, rowValues);
7445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
7455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
7465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
7475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
7485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
7495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Handle a query for the custom request headers registered for a download.
7505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
7515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) {
7525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
7535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       + getDownloadIdFromUri(uri);
7545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
7555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                            Downloads.Impl.RequestHeaders.COLUMN_VALUE};
7565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        Cursor cursor = db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
7575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                 null, null, null, null);
7585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return new ReadOnlyCursorWrapper(cursor);
7595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
7605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
7615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
7625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Delete request headers for downloads matching the given query.
7635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
7645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) {
7655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl._ID};
766b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null);
7675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        try {
7685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
7695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                long id = cursor.getLong(0);
7705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id;
7715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null);
7725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
7735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        } finally {
7745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            cursor.close();
7755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
7765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
7775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
7785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
7793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @return true if we should restrict the columns readable by this caller
7805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
7815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private boolean shouldRestrictVisibility() {
7825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        int callingUid = Binder.getCallingUid();
7835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return Binder.getCallingPid() != Process.myPid() &&
7845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mSystemUid &&
7855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mDefContainerUid &&
7865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Process.supportsProcesses();
7875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
7885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
78957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
7900d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard     * @return a SQL WHERE clause to restrict the query to downloads accessible to the caller's UID
7910d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard     */
7920d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard    private String getRestrictedUidClause() {
7930d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard        return "( " + Constants.UID + "=" +  Binder.getCallingUid() + " OR "
7940d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard                + Downloads.Impl.COLUMN_OTHER_UID + "=" +  Binder.getCallingUid() + " )";
7950d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard    }
7960d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard
7970d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard    /**
79857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Updates a row in the database
79957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
80057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
80157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int update(final Uri uri, final ContentValues values,
80257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String where, final String[] whereArgs) {
8031fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
8041fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(where, sAppReadableColumnsSet);
8051fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
80657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
80757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
80857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
8091fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        boolean startService = false;
8101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
8111fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues;
8121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingPid() != Process.myPid()) {
8131fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = new ContentValues();
8147dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
8157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues);
8167dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL);
8171fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (i != null) {
8187dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i);
8191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                startService = true;
8201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
8217dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
8227dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues);
8237dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues);
8241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        } else {
8251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = values;
826a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            String filename = values.getAsString(Downloads.Impl._DATA);
827a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            if (filename != null) {
828a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                Cursor c = query(uri, new String[]
829a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                        { Downloads.Impl.COLUMN_TITLE }, null, null, null);
830a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                if (!c.moveToFirst() || c.getString(0) == null) {
831a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                    values.put(Downloads.Impl.COLUMN_TITLE,
832a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                            new File(filename).getName());
833a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                }
834a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                c.close();
835a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            }
83671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
83771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS);
83871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING;
83971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            if (isRestart) {
84071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                startService = true;
84171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            }
84257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
8433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
84457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
84557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
8463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
8473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
8483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
8493d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
8503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                String fullWhere = getWhereClause(uri, where, match);
8511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                if (filteredValues.size() > 0) {
852b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    count = db.update(DB_TABLE, filteredValues, fullWhere, whereArgs);
8531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                } else {
8541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    count = 0;
8551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
85657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
8573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
8583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
8593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri);
86057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot update URI: " + uri);
86157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
8623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
8633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
8641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (startService) {
8651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            Context context = getContext();
8661fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            context.startService(new Intent(context, DownloadService.class));
8671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
86857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
86957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
87057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
8713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /**
8723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * Notify of a change through both URIs (/my_downloads and /all_downloads)
8733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uri either URI for the changed download(s)
8743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uriMatch the match ID from {@link #sURIMatcher}
8753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     */
8763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void notifyContentChanged(final Uri uri, int uriMatch) {
8773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Long downloadId = null;
8783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
8793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            downloadId = Long.parseLong(getDownloadIdFromUri(uri));
8803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
8813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        for (Uri uriToNotify : BASE_URIS) {
8823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (downloadId != null) {
8833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                uriToNotify = ContentUris.withAppendedId(uriToNotify, downloadId);
8843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
8853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            getContext().getContentResolver().notifyChange(uriToNotify, null);
8863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
8873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
8883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
8893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private String getWhereClause(final Uri uri, final String where, int uriMatch) {
890b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        StringBuilder myWhere = new StringBuilder();
891b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (where != null) {
892b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            myWhere.append("( " + where + " )");
893b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
8943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
8953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            appendClause(myWhere,
8963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    " ( " + Downloads.Impl._ID + " = " + getDownloadIdFromUri(uri) + " ) ");
897b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
8983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID) {
899b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            appendClause(myWhere, getRestrictedUidClause());
900b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
901b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        return myWhere.toString();
902b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
903b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
90457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
90557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Deletes a row in the database
90657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
90757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
90857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int delete(final Uri uri, final String where,
90957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String[] whereArgs) {
9101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
9111fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(where, sAppReadableColumnsSet);
9121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
91357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
91457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
91557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
91657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
9173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
9183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
9193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
9203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
9213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                String fullWhere = getWhereClause(uri, where, match);
922b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                deleteRequestHeaders(db, fullWhere, whereArgs);
923b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                count = db.delete(DB_TABLE, fullWhere, whereArgs);
92457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
9253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
9273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri);
92857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot delete URI: " + uri);
92957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
9303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
93157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
93257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
93371aab521efba9b28779541440c797220ec98ac97Steve Howard
934b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void appendClause(StringBuilder whereClause, String newClause) {
935b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (whereClause.length() != 0) {
936b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            whereClause.append(" AND ");
937b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
938b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        whereClause.append(newClause);
939b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
94057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
94157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
94257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Remotely opens a file
94357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
94457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
9453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
94657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
9473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseOpenFileInfo(uri, mode);
94857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
9491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
9503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(uri, new String[] {"_data"}, null, null, null);
9513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        String path;
9523d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        try {
9533d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            int count = (cursor != null) ? cursor.getCount() : 0;
9543d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (count != 1) {
9553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                // If there is not exactly one result, throw an appropriate exception.
9563d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (count == 0) {
9573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    throw new FileNotFoundException("No entry for " + uri);
9583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
9593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new FileNotFoundException("Multiple items at " + uri);
9601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
9613d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            cursor.moveToFirst();
9633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            path = cursor.getString(0);
9643d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } finally {
9653d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (cursor != null) {
9663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                cursor.close();
9671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
9681fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
9691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
9701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (path == null) {
9711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("No filename found.");
9721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
9731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (!Helpers.isFilenameValid(path)) {
9741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("Invalid filename.");
9751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
9761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (!"r".equals(mode)) {
9771fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("Bad mode for " + uri + ": " + mode);
9781fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
9793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ParcelFileDescriptor ret = ParcelFileDescriptor.open(new File(path),
9811fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                ParcelFileDescriptor.MODE_READ_ONLY);
9821fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
98357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret == null) {
9841fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (Constants.LOGV) {
9851fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "couldn't open file");
98657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
9871fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("couldn't open file");
98857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
98957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
99057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
99157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseOpenFileInfo(Uri uri, String mode) {
9933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode
9943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                + ", uid: " + Binder.getCallingUid());
9953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(Downloads.Impl.CONTENT_URI,
9963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                new String[] { "_id" }, null, null, "_id");
9973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
9983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.v(Constants.TAG, "null cursor in openFile");
9993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (!cursor.moveToFirst()) {
10013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "empty cursor in openFile");
10023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            } else {
10033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                do {
10043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available");
10053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                } while(cursor.moveToNext());
10063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            cursor.close();
10083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        cursor = query(uri, new String[] { "_data" }, null, null, null);
10103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
10113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.v(Constants.TAG, "null cursor in openFile");
10123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (!cursor.moveToFirst()) {
10143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "empty cursor in openFile");
10153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            } else {
10163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                String filename = cursor.getString(0);
10173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "filename in openFile: " + filename);
10183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (new java.io.File(filename).isFile()) {
10193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    Log.v(Constants.TAG, "file exists in openFile");
10203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
10213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard           cursor.close();
10233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
10253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
10261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyInteger(String key, ContentValues from, ContentValues to) {
10271fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Integer i = from.getAsInteger(key);
10281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (i != null) {
10291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, i);
10301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
10321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyBoolean(String key, ContentValues from, ContentValues to) {
10341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Boolean b = from.getAsBoolean(key);
10351fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (b != null) {
10361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, b);
10371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10381fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
10391fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyString(String key, ContentValues from, ContentValues to) {
10411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        String s = from.getAsString(key);
10421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (s != null) {
10431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, s);
10441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
10461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1047a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    private static final void copyStringWithDefault(String key, ContentValues from,
1048a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            ContentValues to, String defaultValue) {
1049a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyString(key, from, to);
1050a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        if (!to.containsKey(key)) {
1051a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            to.put(key, defaultValue);
1052a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        }
1053a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    }
1054a89321ea04ced76d06f60f5909be203cb654a830Steve Howard
10551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private class ReadOnlyCursorWrapper extends CursorWrapper implements CrossProcessCursor {
10561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public ReadOnlyCursorWrapper(Cursor cursor) {
10571fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            super(cursor);
10581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            mCursor = (CrossProcessCursor) cursor;
10591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean deleteRow() {
10621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new SecurityException("Download manager cursors are read-only");
10631fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean commitUpdates() {
10661fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new SecurityException("Download manager cursors are read-only");
10671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10681fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void fillWindow(int pos, CursorWindow window) {
10701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            mCursor.fillWindow(pos, window);
10711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public CursorWindow getWindow() {
10741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            return mCursor.getWindow();
10751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10771fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean onMove(int oldPosition, int newPosition) {
10781fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            return mCursor.onMove(oldPosition, newPosition);
10791fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10811fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        private CrossProcessCursor mCursor;
10821fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
10831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
108457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project}
1085