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
19ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkeyimport android.app.AppOpsManager;
203ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.app.DownloadManager;
2151cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkeyimport android.app.DownloadManager.Request;
2257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentProvider;
233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howardimport android.content.ContentUris;
2457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentValues;
2557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Context;
2657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Intent;
2757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.UriMatcher;
2891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.ApplicationInfo;
2957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.pm.PackageManager;
3091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.PackageManager.NameNotFoundException;
3157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.Cursor;
323ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.database.DatabaseUtils;
33c6f5aad265cfc36a64cd2bdb5adf3cc9736bbd80Jean-Baptiste Queruimport android.database.SQLException;
3457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
3557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
3657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.net.Uri;
3757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Binder;
38c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport android.os.Handler;
39f775c9840f8f9520ab3dc859597cb34624213363Todd Kennedyimport android.os.HandlerThread;
4057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.ParcelFileDescriptor;
41c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener;
4257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Process;
431d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.provider.BaseColumns;
4457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.provider.Downloads;
459b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport android.provider.OpenableColumns;
46c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkeyimport android.text.TextUtils;
471d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.text.format.DateUtils;
489b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongkerimport android.util.Log;
4957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
50ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkeyimport libcore.io.IoUtils;
51ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
521d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
539b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport com.google.android.collect.Maps;
545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport com.google.common.annotations.VisibleForTesting;
555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.io.File;
571d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.FileDescriptor;
5857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport java.io.FileNotFoundException;
59d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkeyimport java.io.IOException;
601d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.PrintWriter;
61e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.ArrayList;
6201d0182d86db003b2da5b831cb26820093888d9aVasu Noriimport java.util.Arrays;
639b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport java.util.HashMap;
641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.util.HashSet;
65e61798da80558450f580ed948d0d469bd6423d8eSteve Howardimport java.util.Iterator;
66e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.List;
675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport java.util.Map;
681fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
6957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/**
7057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Allows application to interact with the download manager.
7157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
7257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpublic final class DownloadProvider extends ContentProvider {
7357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Database filename */
7457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_NAME = "downloads.db";
751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    /** Current database version */
76c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey    private static final int DB_VERSION = 109;
7757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Name of table in the database */
7857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_TABLE = "downloads";
7957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
8057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for the entire download list */
8157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download";
8257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for an individual download */
8357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download";
8457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
8557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher used to recognize URIs sent by applications */
8657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads belonging to the calling UID */
883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS = 1;
893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of an individual download belonging to the calling UID */
903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS_ID = 2;
913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads in the system */
923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS = 3;
9357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher constant for the URI of an individual download */
943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS_ID = 4;
955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /** URI matcher constant for the URI of a download's request headers */
963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int REQUEST_HEADERS_URI = 5;
973ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori    /** URI matcher constant for the public URI returned by
983ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     * {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file
993ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     * is publicly accessible.
1003ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     */
1013ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori    private static final int PUBLIC_DOWNLOAD_ID = 6;
10257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    static {
1033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
1043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
1053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS);
1063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
1073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
1083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
1103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
1113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
1134bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        // temporary, for backwards compatibility
1144bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS);
1154bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID);
1164bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads",
1174bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1184bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                REQUEST_HEADERS_URI);
1193ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori        sURIMatcher.addURI("downloads",
1203ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#",
1213ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                PUBLIC_DOWNLOAD_ID);
12257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
12357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** Different base URIs that could be used to access an individual download */
1253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final Uri[] BASE_URIS = new Uri[] {
1263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.CONTENT_URI,
1273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
1283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    };
1293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final String[] sAppReadableColumnsArray = new String[] {
1317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._ID,
1327dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_APP_DATA,
1337dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._DATA,
1347dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_MIME_TYPE,
1357dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_VISIBILITY,
1367dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_DESTINATION,
1377dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CONTROL,
1387dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_STATUS,
1397dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_LAST_MODIFICATION,
1407dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
1417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
1427dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TOTAL_BYTES,
1437dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CURRENT_BYTES,
1447dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TITLE,
1450a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        Downloads.Impl.COLUMN_DESCRIPTION,
1460d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard        Downloads.Impl.COLUMN_URI,
14771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
148b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        Downloads.Impl.COLUMN_FILE_NAME_HINT,
149e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
150e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_DELETED,
1519b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        OpenableColumns.DISPLAY_NAME,
1529b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        OpenableColumns.SIZE,
1531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    };
1541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1559b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey    private static final HashSet<String> sAppReadableColumnsSet;
1569b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey    private static final HashMap<String, String> sColumnsMap;
1579b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
1581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    static {
1591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        sAppReadableColumnsSet = new HashSet<String>();
1601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        for (int i = 0; i < sAppReadableColumnsArray.length; ++i) {
1611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]);
1621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
1639b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
1649b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap = Maps.newHashMap();
1659b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap.put(OpenableColumns.DISPLAY_NAME,
1669b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                Downloads.Impl.COLUMN_TITLE + " AS " + OpenableColumns.DISPLAY_NAME);
1679b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap.put(OpenableColumns.SIZE,
1689b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + OpenableColumns.SIZE);
1691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
17001d0182d86db003b2da5b831cb26820093888d9aVasu Nori    private static final List<String> downloadManagerColumnsList =
17101d0182d86db003b2da5b831cb26820093888d9aVasu Nori            Arrays.asList(DownloadManager.UNDERLYING_COLUMNS);
1721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
173c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey    private Handler mHandler;
174c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey
17557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** The database that lies underneath this content provider */
17657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private SQLiteOpenHelper mOpenHelper = null;
17757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
17891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    /** List of uids that can access the downloads */
17991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mSystemUid = -1;
18091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mDefContainerUid = -1;
18191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu
1826d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    @VisibleForTesting
1836d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    SystemFacade mSystemFacade;
1846d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
18557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
186e610c0502c00689411624c00c3f81497df93b202Steve Howard     * This class encapsulates a SQL where clause and its parameters.  It makes it possible for
187e610c0502c00689411624c00c3f81497df93b202Steve Howard     * shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)})
188e610c0502c00689411624c00c3f81497df93b202Steve Howard     * to return both pieces of information, and provides some utility logic to ease piece-by-piece
189e610c0502c00689411624c00c3f81497df93b202Steve Howard     * construction of selections.
190e610c0502c00689411624c00c3f81497df93b202Steve Howard     */
191e610c0502c00689411624c00c3f81497df93b202Steve Howard    private static class SqlSelection {
192e610c0502c00689411624c00c3f81497df93b202Steve Howard        public StringBuilder mWhereClause = new StringBuilder();
193e610c0502c00689411624c00c3f81497df93b202Steve Howard        public List<String> mParameters = new ArrayList<String>();
194e610c0502c00689411624c00c3f81497df93b202Steve Howard
195e610c0502c00689411624c00c3f81497df93b202Steve Howard        public <T> void appendClause(String newClause, final T... parameters) {
196e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (newClause == null || newClause.isEmpty()) {
197e610c0502c00689411624c00c3f81497df93b202Steve Howard                return;
198e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
199e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (mWhereClause.length() != 0) {
200e610c0502c00689411624c00c3f81497df93b202Steve Howard                mWhereClause.append(" AND ");
201e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
202e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append("(");
203e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(newClause);
204e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(")");
205e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (parameters != null) {
206e610c0502c00689411624c00c3f81497df93b202Steve Howard                for (Object parameter : parameters) {
207e610c0502c00689411624c00c3f81497df93b202Steve Howard                    mParameters.add(parameter.toString());
208e610c0502c00689411624c00c3f81497df93b202Steve Howard                }
209e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
210e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
211e610c0502c00689411624c00c3f81497df93b202Steve Howard
212e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String getSelection() {
213e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mWhereClause.toString();
214e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
215e610c0502c00689411624c00c3f81497df93b202Steve Howard
216e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String[] getParameters() {
217e610c0502c00689411624c00c3f81497df93b202Steve Howard            String[] array = new String[mParameters.size()];
218e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mParameters.toArray(array);
219e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
220e610c0502c00689411624c00c3f81497df93b202Steve Howard    }
221e610c0502c00689411624c00c3f81497df93b202Steve Howard
222e610c0502c00689411624c00c3f81497df93b202Steve Howard    /**
22357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Creates and updated database on demand when opening it.
22457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Helper class to create database the first time the provider is
22557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * initialized and upgrade it when a new version of the provider needs
22657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * an updated version of the database.
22757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
22857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private final class DatabaseHelper extends SQLiteOpenHelper {
22957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public DatabaseHelper(final Context context) {
23057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            super(context, DB_NAME, null, DB_VERSION);
23157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
23257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
23357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
23457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Creates database the first time we try to open it.
23557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
23657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
23757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
23857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
2399b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "populating new database");
24057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
2415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            onUpgrade(db, 0, DB_VERSION);
24257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
24357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
24457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
24557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Updates the database format when a content provider is used
24657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * with a database that was created with a different format.
2475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         *
2485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Note: to support downgrades, creating a table should always drop it first if it already
2495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * exists.
25057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
25157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
2521fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) {
2535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (oldV == 31) {
2545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // 31 and 100 are identical, just in different codelines. Upgrading from 31 is the
2555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // same as upgrading from 100.
2565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 100;
2575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV < 100) {
2585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // no logic to upgrade from these older version, just recreate the DB
2599b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV
2605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " to version " + newV + ", which will destroy all old data");
2615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV > newV) {
2635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // user must have downgraded software; we have no way to know how to downgrade the
2645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // DB, so just recreate it
2659b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.i(Constants.TAG, "Downgrading downloads database from version " + oldV
2665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " (current version is " + newV + "), destroying all old data");
2675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (int version = oldV + 1; version <= newV; version++) {
2715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                upgradeTo(db, version);
2725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
2765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Upgrade database from (version - 1) to version.
2775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
2785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void upgradeTo(SQLiteDatabase db, int version) {
2795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            switch (version) {
2805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 100:
2815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createDownloadsTable(db);
2825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 101:
2855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createHeadersTable(db);
2865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2880a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                case 102:
2890a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_PUBLIC_API,
2900a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2910a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_ROAMING,
2920a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2930a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES,
2940a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2950a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    break;
2960a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
29771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                case 103:
29871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
29971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                              "INTEGER NOT NULL DEFAULT 1");
30071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    makeCacheDownloadsInvisible(db);
30171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    break;
30271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
303d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                case 104:
304d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT,
305d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                            "INTEGER NOT NULL DEFAULT 0");
306d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    break;
307d319729622da1893e895f2e35f41d01ecdca3705Steve Howard
30873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                case 105:
30973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    fillNullValues(db);
31073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    break;
31173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
312e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                case 106:
313e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, "TEXT");
314e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_DELETED,
315e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                            "BOOLEAN NOT NULL DEFAULT 0");
316e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    break;
317e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
3189d27069a5453574824860ad3db179599d044e7bdVasu Nori                case 107:
3199d27069a5453574824860ad3db179599d044e7bdVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT");
3209d27069a5453574824860ad3db179599d044e7bdVasu Nori                    break;
3219d27069a5453574824860ad3db179599d044e7bdVasu Nori
322a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                case 108:
323a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_METERED,
324a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                            "INTEGER NOT NULL DEFAULT 1");
325a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                    break;
326a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey
327c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                case 109:
328c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_WRITE,
329c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                            "BOOLEAN NOT NULL DEFAULT 0");
330c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                    break;
331c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey
3325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                default:
3335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalStateException("Don't know how to upgrade to " + version);
3345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
3355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
3365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
3375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
33873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * insert() now ensures these four columns are never null for new downloads, so this method
33973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * makes that true for existing columns, so that code can rely on this assumption.
34073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         */
34173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValues(SQLiteDatabase db) {
34273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            ContentValues values = new ContentValues();
34373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
34473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
34573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
34673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
34773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TITLE, "");
34873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
34973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_DESCRIPTION, "");
35073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
35173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
35273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
35373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValuesForColumn(SQLiteDatabase db, ContentValues values) {
35473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            String column = values.valueSet().iterator().next().getKey();
35573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            db.update(DB_TABLE, values, column + " is null", null);
35673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.clear();
35773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
35873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
35973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        /**
36071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         * Set all existing downloads to the cache partition to be invisible in the downloads UI.
36171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         */
36271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        private void makeCacheDownloadsInvisible(SQLiteDatabase db) {
36371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            ContentValues values = new ContentValues();
36471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false);
36571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            String cacheSelection = Downloads.Impl.COLUMN_DESTINATION
36671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    + " != " + Downloads.Impl.DESTINATION_EXTERNAL;
36771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            db.update(DB_TABLE, values, cacheSelection, null);
36871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
36971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
37071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        /**
3710a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * Add a column to a table using ALTER TABLE.
3720a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param dbTable name of the table
3730a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnName name of the column to add
3740a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnDefinition SQL for the column definition
3750a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         */
3760a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        private void addColumn(SQLiteDatabase db, String dbTable, String columnName,
3770a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                               String columnDefinition) {
3780a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            db.execSQL("ALTER TABLE " + dbTable + " ADD COLUMN " + columnName + " "
3790a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                       + columnDefinition);
3800a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
3810a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
3820a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        /**
3835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Creates the table that'll hold the download information.
3845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
3855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createDownloadsTable(SQLiteDatabase db) {
3865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            try {
3875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
3885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("CREATE TABLE " + DB_TABLE + "(" +
3895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
3905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_URI + " TEXT, " +
3915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, " +
3925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_APP_DATA + " TEXT, " +
3935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NO_INTEGRITY + " BOOLEAN, " +
3945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_FILE_NAME_HINT + " TEXT, " +
395f20af91262fecce05928167123c8d335b4cfd33dVasu Nori                        Constants.OTA_UPDATE + " BOOLEAN, " +
3965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._DATA + " TEXT, " +
3975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_MIME_TYPE + " TEXT, " +
3985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESTINATION + " INTEGER, " +
3995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.NO_SYSTEM_FILES + " BOOLEAN, " +
4005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_VISIBILITY + " INTEGER, " +
4015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CONTROL + " INTEGER, " +
4025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_STATUS + " INTEGER, " +
40312f5dc46aaa8e28cabfbe25d55f0af68f24ab306Jeff Sharkey                        Downloads.Impl.COLUMN_FAILED_CONNECTIONS + " INTEGER, " +
4045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_LAST_MODIFICATION + " BIGINT, " +
4055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + " TEXT, " +
4065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS + " TEXT, " +
4075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + " TEXT, " +
4085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_COOKIE_DATA + " TEXT, " +
4095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_USER_AGENT + " TEXT, " +
4105224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_REFERER + " TEXT, " +
4115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TOTAL_BYTES + " INTEGER, " +
4125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CURRENT_BYTES + " INTEGER, " +
4135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.ETAG + " TEXT, " +
4145224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.UID + " INTEGER, " +
4155224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_OTHER_UID + " INTEGER, " +
4165224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TITLE + " TEXT, " +
4175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESCRIPTION + " TEXT, " +
418495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        Downloads.Impl.COLUMN_MEDIA_SCANNED + " BOOLEAN);");
4195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } catch (SQLException ex) {
4209b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.e(Constants.TAG, "couldn't create table in downloads database");
4215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                throw ex;
4225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
4235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
4245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
4255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createHeadersTable(SQLiteDatabase db) {
4265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("DROP TABLE IF EXISTS " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE);
4275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("CREATE TABLE " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + "(" +
4285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       "id INTEGER PRIMARY KEY AUTOINCREMENT," +
4295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL," +
4305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL," +
4315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL" +
4325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       ");");
43357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
43457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
43557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
43657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
43757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Initializes the content provider when it is created.
43857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
43957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
44057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public boolean onCreate() {
4416d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        if (mSystemFacade == null) {
442af28400b74de05862b470412a5c92f68e99f59f8Steve Howard            mSystemFacade = new RealSystemFacade(getContext());
4436d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        }
4446d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
445f775c9840f8f9520ab3dc859597cb34624213363Todd Kennedy        HandlerThread handlerThread =
446f775c9840f8f9520ab3dc859597cb34624213363Todd Kennedy                new HandlerThread("DownloadProvider handler", Process.THREAD_PRIORITY_BACKGROUND);
447f775c9840f8f9520ab3dc859597cb34624213363Todd Kennedy        handlerThread.start();
448f775c9840f8f9520ab3dc859597cb34624213363Todd Kennedy        mHandler = new Handler(handlerThread.getLooper());
449c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey
45057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        mOpenHelper = new DatabaseHelper(getContext());
45191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the system uid
45291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        mSystemUid = Process.SYSTEM_UID;
45391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the default container uid. Package name hardcoded
45491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // for now.
45591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        ApplicationInfo appInfo = null;
45691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        try {
45791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            appInfo = getContext().getPackageManager().
45891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu                    getApplicationInfo("com.android.defcontainer", 0);
45991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        } catch (NameNotFoundException e) {
4609b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.wtf(Constants.TAG, "Could not get ApplicationInfo for com.android.defconatiner", e);
46191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
46291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        if (appInfo != null) {
46391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            mDefContainerUid = appInfo.uid;
46491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
4655218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori        // start the DownloadService class. don't wait for the 1st download to be issued.
4665218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori        // saves us by getting some initialization code in DownloadService out of the way.
4675218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori        Context context = getContext();
4685218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori        context.startService(new Intent(context, DownloadService.class));
46957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return true;
47057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
47157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
47257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
47357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Returns the content-provider-style MIME types of the various
47457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * types accessible through this content provider.
47557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
47657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
47757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public String getType(final Uri uri) {
47857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
47957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
4809b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case MY_DOWNLOADS:
4819b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case ALL_DOWNLOADS: {
48257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_LIST_TYPE;
48357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
4849b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case MY_DOWNLOADS_ID:
485c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey            case ALL_DOWNLOADS_ID:
4863ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori            case PUBLIC_DOWNLOAD_ID: {
4873ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                // return the mimetype of this id from the database
4883ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                final String id = getDownloadIdFromUri(uri);
4893ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
490c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                final String mimeType = DatabaseUtils.stringForQuery(db,
4913ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE +
4923ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        " WHERE " + Downloads.Impl._ID + " = ?",
4933ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        new String[]{id});
494c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                if (TextUtils.isEmpty(mimeType)) {
495c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                    return DOWNLOAD_TYPE;
496c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                } else {
497c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                    return mimeType;
498c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                }
4993ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori            }
50057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            default: {
50157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                if (Constants.LOGV) {
5029b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                    Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri);
50357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                }
50457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new IllegalArgumentException("Unknown URI: " + uri);
50557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
50657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
50757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
50857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
50957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
51057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Inserts a row in the database
51157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
51257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
51357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public Uri insert(final Uri uri, final ContentValues values) {
514b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        checkInsertPermissions(values);
51557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
51657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        // note we disallow inserting into ALL_DOWNLOADS
5183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        int match = sURIMatcher.match(uri);
5193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match != MY_DOWNLOADS) {
5209b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri);
52157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
52257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
52357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
524b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some of the input values as it
5251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues = new ContentValues();
5267dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_URI, values, filteredValues);
5277dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
5287dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues);
5297dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues);
5307dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues);
53171aab521efba9b28779541440c797220ec98ac97Steve Howard        copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues);
532b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
53371aab521efba9b28779541440c797220ec98ac97Steve Howard        boolean isPublicApi =
53471aab521efba9b28779541440c797220ec98ac97Steve Howard                values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE;
53571aab521efba9b28779541440c797220ec98ac97Steve Howard
536b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the destination column
5377dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION);
538ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (dest != null) {
539dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey            if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
5401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    != PackageManager.PERMISSION_GRANTED
541b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                    && (dest == Downloads.Impl.DESTINATION_CACHE_PARTITION
542b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                            || dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING
543b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                            || dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION)) {
5449aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                throw new SecurityException("setting destination to : " + dest +
5459aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                        " not allowed, unless PERMISSION_ACCESS_ADVANCED is granted");
5461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
54771aab521efba9b28779541440c797220ec98ac97Steve Howard            // for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically
54871aab521efba9b28779541440c797220ec98ac97Steve Howard            // switch to non-purgeable download
54971aab521efba9b28779541440c797220ec98ac97Steve Howard            boolean hasNonPurgeablePermission =
550dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey                    getContext().checkCallingOrSelfPermission(
55171aab521efba9b28779541440c797220ec98ac97Steve Howard                            Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE)
55271aab521efba9b28779541440c797220ec98ac97Steve Howard                            == PackageManager.PERMISSION_GRANTED;
55371aab521efba9b28779541440c797220ec98ac97Steve Howard            if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
55471aab521efba9b28779541440c797220ec98ac97Steve Howard                    && hasNonPurgeablePermission) {
55571aab521efba9b28779541440c797220ec98ac97Steve Howard                dest = Downloads.Impl.DESTINATION_CACHE_PARTITION;
55671aab521efba9b28779541440c797220ec98ac97Steve Howard            }
5576d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            if (dest == Downloads.Impl.DESTINATION_FILE_URI) {
558b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                checkFileUriDestination(values);
559ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
560ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            } else if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
561ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                getContext().enforceCallingOrSelfPermission(
562ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
563ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        "No permission to write");
564ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
565ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
566b8bc2a76ddaa0c59d681913dba10fd7ec0a2a22bJeff Sharkey                if (appOps.noteProxyOp(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
567ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
568ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    throw new SecurityException("No permission to write");
569ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                }
570ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
5719aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori            } else if (dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
5729aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                getContext().enforcePermission(
5739aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                        android.Manifest.permission.ACCESS_CACHE_FILESYSTEM,
5749aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                        Binder.getCallingPid(), Binder.getCallingUid(),
5759aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                        "need ACCESS_CACHE_FILESYSTEM permission to use system cache");
5766d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            }
5777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);
578ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        }
579b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
580b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the visibility column
5817dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY);
582ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (vis == null) {
5837dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
5847dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
5857dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
586ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru            } else {
5877dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
5887dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_HIDDEN);
5891fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
590ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        } else {
5917dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis);
59257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
593b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy the control column as is
5947dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
595b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
596b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        /*
597b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori         * requests coming from
5989b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori         * DownloadManager.addCompletedDownload(String, String, String,
5999b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori         * boolean, String, String, long) need special treatment
600b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori         */
601b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
602b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
603b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            // these requests always are marked as 'completed'
604b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);
605b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES,
606b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                    values.getAsLong(Downloads.Impl.COLUMN_TOTAL_BYTES));
607b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
608b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            copyInteger(Downloads.Impl.COLUMN_MEDIA_SCANNED, values, filteredValues);
609b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            copyString(Downloads.Impl._DATA, values, filteredValues);
610c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            copyBoolean(Downloads.Impl.COLUMN_ALLOW_WRITE, values, filteredValues);
611b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        } else {
612b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
613b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
614b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
615b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        }
616b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
617b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // set lastupdate to current time
6182c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori        long lastMod = mSystemFacade.currentTimeMillis();
6192c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori        filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, lastMod);
6200a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
621b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // use packagename of the caller to set the notification columns
6227dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
6237dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
6240a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (pckg != null && (clazz != null || isPublicApi)) {
6251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            int uid = Binder.getCallingUid();
6261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            try {
6270a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) {
6287dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg);
6290a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    if (clazz != null) {
6300a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                        filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz);
6310a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    }
6321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
6331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } catch (PackageManager.NameNotFoundException ex) {
6341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                /* ignored for now */
63557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
63657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
637b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
638b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some more columns as is
6397dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues);
6407dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues);
6417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues);
6427dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues);
643b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
644b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // UID, PID columns
645dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
6461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                == PackageManager.PERMISSION_GRANTED) {
6477dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues);
64857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
6491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        filteredValues.put(Constants.UID, Binder.getCallingUid());
6501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingUid() == 0) {
6511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            copyInteger(Constants.UID, values, filteredValues);
65257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
653b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
654b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some more columns as is
655a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, "");
656a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, "");
65757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
658b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // is_visible_in_downloads_ui column
65971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) {
66071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues);
66171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        } else {
66271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            // by default, make external downloads visible in the UI
66371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL);
66471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal);
66571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
66671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
667b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // public api requests and networktypes/roaming columns
6680a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (isPublicApi) {
6690a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);
6700a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);
671a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey            copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues);
6720a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
6730a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
6741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Constants.LOGVV) {
6759b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "initiating download with UID "
6761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    + filteredValues.getAsInteger(Constants.UID));
6777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) {
6789b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "other UID " +
6797dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID));
6801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
68157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
68257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        long rowID = db.insert(DB_TABLE, null, filteredValues);
6843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (rowID == -1) {
6859b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.d(Constants.TAG, "couldn't insert into downloads database");
6863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return null;
68757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
68857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        insertRequestHeaders(db, rowID, values);
6903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
6911225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey
6921225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey        // Always start service to handle notifications and/or scanning
6931225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey        final Context context = getContext();
6941225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey        context.startService(new Intent(context, DownloadService.class));
6951225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey
6963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
69757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
69857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
69957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
700b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Check that the file URI provided for DESTINATION_FILE_URI is valid.
701b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
702b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkFileUriDestination(ContentValues values) {
703b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String fileUri = values.getAsString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
704b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (fileUri == null) {
705b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException(
706b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    "DESTINATION_FILE_URI must include a file URI under COLUMN_FILE_NAME_HINT");
707b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
708b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Uri uri = Uri.parse(fileUri);
7095d81e2447ed77860afecd71583e137178c2c6807Steve Howard        String scheme = uri.getScheme();
7105d81e2447ed77860afecd71583e137178c2c6807Steve Howard        if (scheme == null || !scheme.equals("file")) {
711b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException("Not a file URI: " + uri);
712b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
713d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey        final String path = uri.getPath();
714b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        if (path == null) {
715b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard            throw new IllegalArgumentException("Invalid file URI: " + uri);
716b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        }
717ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
718ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        final File file = new File(path);
719ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        if (Helpers.isFilenameValidInExternalPackage(getContext(), file, getCallingPackage())) {
720ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            // No permissions required for paths belonging to calling package
721ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            return;
722ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        } else if (Helpers.isFilenameValidInExternal(getContext(), file)) {
723ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            // Otherwise we require write permission
724ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            getContext().enforceCallingOrSelfPermission(
725ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
726ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    "No permission to write to " + file);
727ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
728ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
729b8bc2a76ddaa0c59d681913dba10fd7ec0a2a22bJeff Sharkey            if (appOps.noteProxyOp(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
730ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
731ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                throw new SecurityException("No permission to write to " + file);
732d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey            }
733ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
734ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        } else {
735ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            throw new SecurityException("Unsupported path " + file);
736b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
737b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
738b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
739b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
740b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to
741b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * constraints in the rest of the code. Apps without that may still access this provider through
742b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * the public API, but additional restrictions are imposed. We check those restrictions here.
743b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     *
744b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @param values ContentValues provided to insert()
745b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @throws SecurityException if the caller has insufficient permissions
746b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
747b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkInsertPermissions(ContentValues values) {
748b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS)
749b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                == PackageManager.PERMISSION_GRANTED) {
750b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            return;
751b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
752b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
753b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
754b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                "INTERNET permission is required to use the download manager");
755b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
756b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // ensure the request fits within the bounds of a public API request
757b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // first copy so we can remove values
758b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values = new ContentValues(values);
759b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
760b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // check columns whose values are restricted
761b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_IS_PUBLIC_API, Boolean.TRUE);
762b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
763b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the destination column
764b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
765b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
766b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            /* this row is inserted by
7679b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori             * DownloadManager.addCompletedDownload(String, String, String,
7689b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori             * boolean, String, String, long)
769b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori             */
770b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl.COLUMN_TOTAL_BYTES);
771b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl._DATA);
772b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl.COLUMN_STATUS);
773b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        }
774b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION,
775b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE,
776b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_FILE_URI,
777b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
7789da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard
7799da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION)
7809da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                == PackageManager.PERMISSION_GRANTED) {
7819da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
78251cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_HIDDEN,
78351cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE,
78451cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED,
78551cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
7869da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        } else {
7879da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
78851cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE,
78951cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED,
79051cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
7919da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        }
792b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
793b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // remove the rest of the columns that are allowed (with any value)
794b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_URI);
795b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_TITLE);
796b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_DESCRIPTION);
797b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_MIME_TYPE);
798b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_FILE_NAME_HINT); // checked later in insert()
799b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert()
800b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
801b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
802a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey        values.remove(Downloads.Impl.COLUMN_ALLOW_METERED);
80371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
8043002e1870d4ad76c260b4729a8d86212c8db3e78Vasu Nori        values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED);
805c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        values.remove(Downloads.Impl.COLUMN_ALLOW_WRITE);
806e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator();
807e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        while (iterator.hasNext()) {
808e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            String key = iterator.next().getKey();
809e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
810e61798da80558450f580ed948d0d469bd6423d8eSteve Howard                iterator.remove();
811e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            }
812e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        }
813b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
814b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // any extra columns are extraneous and disallowed
815b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (values.size() > 0) {
816b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            StringBuilder error = new StringBuilder("Invalid columns in request: ");
817b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            boolean first = true;
818b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            for (Map.Entry<String, Object> entry : values.valueSet()) {
819b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                if (!first) {
820b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    error.append(", ");
821b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                }
822b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                error.append(entry.getKey());
823b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
824b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException(error.toString());
825b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
826b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
827b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
828b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
829b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Remove column from values, and throw a SecurityException if the value isn't within the
830b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * specified allowedValues.
831b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
832b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void enforceAllowedValues(ContentValues values, String column,
833b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            Object... allowedValues) {
834b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Object value = values.get(column);
835b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(column);
836b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        for (Object allowedValue : allowedValues) {
837b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value == null && allowedValue == null) {
838b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
839b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
840b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value != null && value.equals(allowedValue)) {
841b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
842b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
843b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
844b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        throw new SecurityException("Invalid value for " + column + ": " + value);
845b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
846b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
8472e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey    private Cursor queryCleared(Uri uri, String[] projection, String selection,
8482e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            String[] selectionArgs, String sort) {
8492e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        final long token = Binder.clearCallingIdentity();
8502e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        try {
8512e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            return query(uri, projection, selection, selectionArgs, sort);
8522e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        } finally {
8532e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            Binder.restoreCallingIdentity(token);
8542e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        }
8552e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey    }
8562e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey
857b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
85857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Starts a database query
85957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
86057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
8611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    public Cursor query(final Uri uri, String[] projection,
86257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String selection, final String[] selectionArgs,
86357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String sort) {
8641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
8651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(selection, sAppReadableColumnsSet);
8661fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
86757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
86857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
86957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
8703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == -1) {
8713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (Constants.LOGV) {
8729b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "querying unknown URI: " + uri);
87357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
8743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            throw new IllegalArgumentException("Unknown URI: " + uri);
8753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
8763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
8773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == REQUEST_HEADERS_URI) {
8783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (projection != null || selection != null || sort != null) {
8793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new UnsupportedOperationException("Request header queries do not support "
8803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                                                        + "projections, selections or sorting");
88157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
8823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return queryRequestHeaders(db, uri);
8833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
8843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
885e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection fullSelection = getWhereClause(uri, selection, selectionArgs, match);
88657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
8875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        if (shouldRestrictVisibility()) {
8881fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (projection == null) {
8899b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                projection = sAppReadableColumnsArray.clone();
8901fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } else {
89101d0182d86db003b2da5b831cb26820093888d9aVasu Nori                // check the validity of the columns in projection
8921fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                for (int i = 0; i < projection.length; ++i) {
89301d0182d86db003b2da5b831cb26820093888d9aVasu Nori                    if (!sAppReadableColumnsSet.contains(projection[i]) &&
89401d0182d86db003b2da5b831cb26820093888d9aVasu Nori                            !downloadManagerColumnsList.contains(projection[i])) {
8951fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                        throw new IllegalArgumentException(
8961fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                                "column " + projection[i] + " is not allowed in queries");
8971fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    }
8981fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
8991fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
9009b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
9019b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            for (int i = 0; i < projection.length; i++) {
9029b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                final String newColumn = sColumnsMap.get(projection[i]);
9039b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                if (newColumn != null) {
9049b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                    projection[i] = newColumn;
9059b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                }
9069b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            }
90757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
90857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
90957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
9103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseQueryInfo(projection, selection, selectionArgs, sort, db);
91157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
91257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
913e610c0502c00689411624c00c3f81497df93b202Steve Howard        Cursor ret = db.query(DB_TABLE, projection, fullSelection.getSelection(),
914e610c0502c00689411624c00c3f81497df93b202Steve Howard                fullSelection.getParameters(), null, null, sort);
91557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
91657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret != null) {
91757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            ret.setNotificationUri(getContext().getContentResolver(), uri);
91857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
9199b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG,
92057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                        "created cursor " + ret + " on behalf of " + Binder.getCallingPid());
92157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
92257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        } else {
92357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGV) {
9249b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "query failed in downloads database");
92557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
92657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
92757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
92857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
92957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
93057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseQueryInfo(String[] projection, final String selection,
9323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            final String[] selectionArgs, final String sort, SQLiteDatabase db) {
9333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        java.lang.StringBuilder sb = new java.lang.StringBuilder();
9343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("starting query, database is ");
9353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (db != null) {
9363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("not ");
9373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("null; ");
9393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (projection == null) {
9403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is null; ");
9413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (projection.length == 0) {
9423d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is empty; ");
9433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
9443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < projection.length; ++i) {
9453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("projection[");
9463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
9473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
9483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(projection[i]);
9493d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
9503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
9513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9523d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("selection is ");
9533d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(selection);
9543d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("; ");
9553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (selectionArgs == null) {
9563d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is null; ");
9573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (selectionArgs.length == 0) {
9583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is empty; ");
9593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
9603d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < selectionArgs.length; ++i) {
9613d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("selectionArgs[");
9623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
9633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
9643d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(selectionArgs[i]);
9653d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
9663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
9673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("sort is ");
9693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(sort);
9703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(".");
9719b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker        Log.v(Constants.TAG, sb.toString());
9723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
9733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private String getDownloadIdFromUri(final Uri uri) {
9755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return uri.getPathSegments().get(1);
9765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
9775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
9785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
9795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Insert request headers for a download into the DB.
9805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
9815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void insertRequestHeaders(SQLiteDatabase db, long downloadId, ContentValues values) {
9825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        ContentValues rowValues = new ContentValues();
9835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID, downloadId);
9845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        for (Map.Entry<String, Object> entry : values.valueSet()) {
9855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            String key = entry.getKey();
9865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
9875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String headerLine = entry.getValue().toString();
9885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                if (!headerLine.contains(":")) {
9895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalArgumentException("Invalid HTTP header line: " + headerLine);
9905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                }
9915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String[] parts = headerLine.split(":", 2);
9925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_HEADER, parts[0].trim());
9935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_VALUE, parts[1].trim());
9945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.insert(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, null, rowValues);
9955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
9965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
9975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
9985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
9995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Handle a query for the custom request headers registered for a download.
10015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) {
10035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
10045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       + getDownloadIdFromUri(uri);
10055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
10065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                            Downloads.Impl.RequestHeaders.COLUMN_VALUE};
100753356ad554f53a093434161cb494b8e818c294fbSteve Howard        return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
100853356ad554f53a093434161cb494b8e818c294fbSteve Howard                        null, null, null, null);
10095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10105224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Delete request headers for downloads matching the given query.
10135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10145224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) {
10155224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl._ID};
1016b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null);
10175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        try {
10185224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
10195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                long id = cursor.getLong(0);
10205224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id;
10215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null);
10225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
10235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        } finally {
10245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            cursor.close();
10255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
10265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @return true if we should restrict the columns readable by this caller
10305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private boolean shouldRestrictVisibility() {
10325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        int callingUid = Binder.getCallingUid();
10335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return Binder.getCallingPid() != Process.myPid() &&
10345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mSystemUid &&
103512c7bd488afe3a611d665e364e361f04f4a8c8d8Jeff Brown                callingUid != mDefContainerUid;
10365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
103857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
103957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Updates a row in the database
104057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
104157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
104257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int update(final Uri uri, final ContentValues values,
104357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String where, final String[] whereArgs) {
10441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(where, sAppReadableColumnsSet);
10461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
104757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
104857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
104957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
10501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        boolean startService = false;
10511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1052e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        if (values.containsKey(Downloads.Impl.COLUMN_DELETED)) {
1053e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            if (values.getAsInteger(Downloads.Impl.COLUMN_DELETED) == 1) {
1054e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                // some rows are to be 'deleted'. need to start DownloadService.
1055e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                startService = true;
1056e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            }
1057e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        }
1058e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
10591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues;
10601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingPid() != Process.myPid()) {
10611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = new ContentValues();
10627dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
10637dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues);
10647dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL);
10651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (i != null) {
10667dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i);
10671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                startService = true;
10681fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
1069e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
10707dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
10717dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues);
1072e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, values, filteredValues);
10737dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues);
1074e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyInteger(Downloads.Impl.COLUMN_DELETED, values, filteredValues);
10751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        } else {
10761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = values;
1077a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            String filename = values.getAsString(Downloads.Impl._DATA);
1078a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            if (filename != null) {
1079a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                Cursor c = null;
1080a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                try {
1081a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    c = query(uri, new String[]
1082a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                            { Downloads.Impl.COLUMN_TITLE }, null, null, null);
1083a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    if (!c.moveToFirst() || c.getString(0).isEmpty()) {
1084a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        values.put(Downloads.Impl.COLUMN_TITLE, new File(filename).getName());
1085a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    }
1086a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } finally {
1087a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    IoUtils.closeQuietly(c);
1088a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                }
1089a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            }
109071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
109171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS);
109271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING;
1093d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            boolean isUserBypassingSizeLimit =
1094d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                values.containsKey(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
1095d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            if (isRestart || isUserBypassingSizeLimit) {
109671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                startService = true;
109771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            }
109857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
10993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
110057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
110157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
11023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
11033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
11043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
11053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
1106e610c0502c00689411624c00c3f81497df93b202Steve Howard                SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
11071fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                if (filteredValues.size() > 0) {
1108e610c0502c00689411624c00c3f81497df93b202Steve Howard                    count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
1109e610c0502c00689411624c00c3f81497df93b202Steve Howard                            selection.getParameters());
11101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                } else {
11111fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    count = 0;
11121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
111357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
11143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
11153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
11169b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri);
111757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot update URI: " + uri);
111857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
11193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
11203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
11211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (startService) {
11221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            Context context = getContext();
11231fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            context.startService(new Intent(context, DownloadService.class));
11241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
112557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
112657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
112757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
11283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /**
11293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * Notify of a change through both URIs (/my_downloads and /all_downloads)
11303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uri either URI for the changed download(s)
11313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uriMatch the match ID from {@link #sURIMatcher}
11323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     */
11333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void notifyContentChanged(final Uri uri, int uriMatch) {
11343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Long downloadId = null;
11353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
11363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            downloadId = Long.parseLong(getDownloadIdFromUri(uri));
11373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
11383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        for (Uri uriToNotify : BASE_URIS) {
11393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (downloadId != null) {
11403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                uriToNotify = ContentUris.withAppendedId(uriToNotify, downloadId);
11413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
11423d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            getContext().getContentResolver().notifyChange(uriToNotify, null);
11433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
11443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
11453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1146e610c0502c00689411624c00c3f81497df93b202Steve Howard    private SqlSelection getWhereClause(final Uri uri, final String where, final String[] whereArgs,
1147e610c0502c00689411624c00c3f81497df93b202Steve Howard            int uriMatch) {
1148e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection selection = new SqlSelection();
1149e610c0502c00689411624c00c3f81497df93b202Steve Howard        selection.appendClause(where, whereArgs);
11503ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID ||
11513ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                uriMatch == PUBLIC_DOWNLOAD_ID) {
1152e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri));
1153b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
1154e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root        if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID)
1155dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey                && getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ALL)
1156e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root                != PackageManager.PERMISSION_GRANTED) {
1157e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(
1158e610c0502c00689411624c00c3f81497df93b202Steve Howard                    Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?",
1159bff4fe9b858d95e984298d4863a5199f1ee2c54eJeff Sharkey                    Binder.getCallingUid(), Binder.getCallingUid());
1160b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
1161e610c0502c00689411624c00c3f81497df93b202Steve Howard        return selection;
1162b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
1163b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
116457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
116557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Deletes a row in the database
116657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
116757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
1168e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey    public int delete(final Uri uri, final String where, final String[] whereArgs) {
11691f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey        if (shouldRestrictVisibility()) {
11701f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey            Helpers.validateSelection(where, sAppReadableColumnsSet);
11711f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey        }
11721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
117357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
117457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
117557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
117657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
11773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
11783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
11793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
11803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
1181e610c0502c00689411624c00c3f81497df93b202Steve Howard                SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
1182e610c0502c00689411624c00c3f81497df93b202Steve Howard                deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
1183afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey
1184afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                final Cursor cursor = db.query(DB_TABLE, new String[] {
1185e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        Downloads.Impl._ID, Downloads.Impl._DATA
1186e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                }, selection.getSelection(), selection.getParameters(), null, null, null);
1187afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                try {
1188afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                    while (cursor.moveToNext()) {
1189afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                        final long id = cursor.getLong(0);
1190afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                        DownloadStorageProvider.onDownloadProviderDelete(getContext(), id);
1191e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey
1192e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        final String path = cursor.getString(1);
1193e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        if (!TextUtils.isEmpty(path)) {
1194e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                            final File file = new File(path);
1195e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                            if (Helpers.isFilenameValid(getContext(), file)) {
1196e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                                Log.v(Constants.TAG, "Deleting " + file + " via provider delete");
1197e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                                file.delete();
1198e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                            }
1199e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        }
1200afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                    }
1201afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                } finally {
1202afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                    IoUtils.closeQuietly(cursor);
1203afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                }
1204afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey
1205e610c0502c00689411624c00c3f81497df93b202Steve Howard                count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters());
120657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
12073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
12083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
12099b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri);
121057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot delete URI: " + uri);
121157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
12123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
121357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
121457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
121571aab521efba9b28779541440c797220ec98ac97Steve Howard
121657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
121757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Remotely opens a file
121857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
121957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
1220c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey    public ParcelFileDescriptor openFile(final Uri uri, String mode) throws FileNotFoundException {
122157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
12223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseOpenFileInfo(uri, mode);
122357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
12241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
12252e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        final Cursor cursor = queryCleared(uri, new String[] {
1226495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                Downloads.Impl._DATA, Downloads.Impl.COLUMN_STATUS,
1227495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.COLUMN_MEDIA_SCANNED }, null,
1228495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                null, null);
1229495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final String path;
1230495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final boolean shouldScan;
12313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        try {
12323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            int count = (cursor != null) ? cursor.getCount() : 0;
12333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (count != 1) {
12343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                // If there is not exactly one result, throw an appropriate exception.
12353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (count == 0) {
12363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    throw new FileNotFoundException("No entry for " + uri);
12373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
12383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new FileNotFoundException("Multiple items at " + uri);
12391fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
12403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1241495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            if (cursor.moveToFirst()) {
1242495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int status = cursor.getInt(1);
1243495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int destination = cursor.getInt(2);
1244495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int mediaScanned = cursor.getInt(3);
1245495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey
1246495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                path = cursor.getString(0);
1247495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                shouldScan = Downloads.Impl.isStatusSuccess(status) && (
1248495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        destination == Downloads.Impl.DESTINATION_EXTERNAL
1249495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        || destination == Downloads.Impl.DESTINATION_FILE_URI
1250495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        || destination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
1251495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        && mediaScanned != 2;
1252495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            } else {
1253495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                throw new FileNotFoundException("Failed moveToFirst");
1254495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            }
12553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } finally {
1256c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            IoUtils.closeQuietly(cursor);
12571fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
12581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
12591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (path == null) {
12601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("No filename found.");
12611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
12623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1263c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        final File file = new File(path);
1264dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        if (!Helpers.isFilenameValid(getContext(), file)) {
1265dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey            throw new FileNotFoundException("Invalid file: " + file);
1266dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        }
1267dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey
1268495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final int pfdMode = ParcelFileDescriptor.parseMode(mode);
1269495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
1270495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            return ParcelFileDescriptor.open(file, pfdMode);
1271c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        } else {
1272c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            try {
1273c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                // When finished writing, update size and timestamp
1274495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                return ParcelFileDescriptor.open(file, pfdMode, mHandler, new OnCloseListener() {
1275495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    @Override
1276495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    public void onClose(IOException e) {
1277495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        final ContentValues values = new ContentValues();
1278495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, file.length());
1279495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION,
1280495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                                System.currentTimeMillis());
1281495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        update(uri, values, null, null);
1282495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey
1283495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        if (shouldScan) {
1284495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            final Intent intent = new Intent(
1285495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                                    Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
1286495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            intent.setData(Uri.fromFile(file));
1287495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            getContext().sendBroadcast(intent);
1288495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        }
1289495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    }
1290495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                });
1291c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            } catch (IOException e) {
1292c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                throw new FileNotFoundException("Failed to open for writing: " + e);
1293c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            }
1294c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        }
129557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
129657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
12971d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    @Override
12981d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
12991d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 120);
13001d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
13011d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.println("Downloads updated in last hour:");
13021d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.increaseIndent();
13031d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
13041d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
13051d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final long modifiedAfter = mSystemFacade.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS;
13061d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final Cursor cursor = db.query(DB_TABLE, null,
13071d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                Downloads.Impl.COLUMN_LAST_MODIFICATION + ">" + modifiedAfter, null, null, null,
13081d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                Downloads.Impl._ID + " ASC");
13091d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        try {
13101d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            final String[] cols = cursor.getColumnNames();
13111d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            final int idCol = cursor.getColumnIndex(BaseColumns._ID);
13121d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            while (cursor.moveToNext()) {
13131d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.println("Download #" + cursor.getInt(idCol) + ":");
13141d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.increaseIndent();
13151d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                for (int i = 0; i < cols.length; i++) {
13161d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    // Omit sensitive data when dumping
13171d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    if (Downloads.Impl.COLUMN_COOKIE_DATA.equals(cols[i])) {
13181d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                        continue;
13191d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    }
13201d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    pw.printPair(cols[i], cursor.getString(i));
13211d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                }
13221d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.println();
13231d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.decreaseIndent();
13241d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            }
13251d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        } finally {
13261d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            cursor.close();
13271d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        }
13281d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
13291d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.decreaseIndent();
13301d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    }
13311d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
13323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseOpenFileInfo(Uri uri, String mode) {
13339b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker        Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode
13343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                + ", uid: " + Binder.getCallingUid());
13353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(Downloads.Impl.CONTENT_URI,
13363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                new String[] { "_id" }, null, null, "_id");
13373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
13389b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "null cursor in openFile");
13393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
1340a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            try {
1341a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                if (!cursor.moveToFirst()) {
1342a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "empty cursor in openFile");
1343a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } else {
1344a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    do {
1345a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available");
1346a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    } while(cursor.moveToNext());
1347a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                }
1348a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            } finally {
1349a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                cursor.close();
13503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
13513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
13523d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        cursor = query(uri, new String[] { "_data" }, null, null, null);
13533d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
13549b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "null cursor in openFile");
13553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
1356a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            try {
1357a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                if (!cursor.moveToFirst()) {
1358a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "empty cursor in openFile");
1359a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } else {
1360a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    String filename = cursor.getString(0);
1361a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "filename in openFile: " + filename);
1362a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    if (new java.io.File(filename).isFile()) {
1363a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        Log.v(Constants.TAG, "file exists in openFile");
1364a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    }
13653d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
1366a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            } finally {
1367a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                cursor.close();
13683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
13693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
13703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
13713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
13721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyInteger(String key, ContentValues from, ContentValues to) {
13731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Integer i = from.getAsInteger(key);
13741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (i != null) {
13751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, i);
13761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
13771fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
13781fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13791fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyBoolean(String key, ContentValues from, ContentValues to) {
13801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Boolean b = from.getAsBoolean(key);
13811fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (b != null) {
13821fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, b);
13831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
13841fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
13851fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13861fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyString(String key, ContentValues from, ContentValues to) {
13871fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        String s = from.getAsString(key);
13881fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (s != null) {
13891fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, s);
13901fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
13911fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
13921fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1393a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    private static final void copyStringWithDefault(String key, ContentValues from,
1394a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            ContentValues to, String defaultValue) {
1395a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyString(key, from, to);
1396a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        if (!to.containsKey(key)) {
1397a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            to.put(key, defaultValue);
1398a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        }
1399a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    }
140057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project}
1401