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
193a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport static android.provider.BaseColumns._ID;
20053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkeyimport static android.provider.Downloads.Impl.COLUMN_DESTINATION;
21053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkeyimport static android.provider.Downloads.Impl.COLUMN_MEDIA_SCANNED;
22053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkeyimport static android.provider.Downloads.Impl.COLUMN_MIME_TYPE;
23053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkeyimport static android.provider.Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD;
243a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport static android.provider.Downloads.Impl._DATA;
253a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
26ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkeyimport android.app.AppOpsManager;
273ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.app.DownloadManager;
2851cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkeyimport android.app.DownloadManager.Request;
293a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport android.app.job.JobScheduler;
3057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentProvider;
31efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkeyimport android.content.ContentResolver;
323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howardimport android.content.ContentUris;
3357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentValues;
3457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Context;
3557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Intent;
3657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.UriMatcher;
3791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.ApplicationInfo;
3857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.pm.PackageManager;
3991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.PackageManager.NameNotFoundException;
4057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.Cursor;
413ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.database.DatabaseUtils;
42c6f5aad265cfc36a64cd2bdb5adf3cc9736bbd80Jean-Baptiste Queruimport android.database.SQLException;
4357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
4457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
45b759707b80987d0cb4ad2a3a78c11702a45a36c2Ben Linimport android.database.sqlite.SQLiteQueryBuilder;
4657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.net.Uri;
4757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Binder;
4857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.ParcelFileDescriptor;
49c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener;
5057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Process;
511d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.provider.BaseColumns;
5257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.provider.Downloads;
539b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport android.provider.OpenableColumns;
54c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkeyimport android.text.TextUtils;
551d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.text.format.DateUtils;
569b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongkerimport android.util.Log;
5757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
583a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkeyimport com.android.internal.util.IndentingPrintWriter;
593a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
60ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkeyimport libcore.io.IoUtils;
61ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
629b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport com.google.android.collect.Maps;
635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport com.google.common.annotations.VisibleForTesting;
645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.io.File;
661d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.FileDescriptor;
6757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport java.io.FileNotFoundException;
68d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkeyimport java.io.IOException;
691d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.PrintWriter;
70e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.ArrayList;
7101d0182d86db003b2da5b831cb26820093888d9aVasu Noriimport java.util.Arrays;
729b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport java.util.HashMap;
731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.util.HashSet;
74e61798da80558450f580ed948d0d469bd6423d8eSteve Howardimport java.util.Iterator;
75e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.List;
765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport java.util.Map;
771fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
7857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/**
7957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Allows application to interact with the download manager.
8057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
8157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpublic final class DownloadProvider extends ContentProvider {
8257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Database filename */
8357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_NAME = "downloads.db";
841fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    /** Current database version */
853a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey    private static final int DB_VERSION = 110;
8657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Name of table in the database */
8757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_TABLE = "downloads";
88635e12dcdf24215246615c78c5de6b883cb00a0fFyodor Kupolov    /** Memory optimization - close idle connections after 30s of inactivity */
89635e12dcdf24215246615c78c5de6b883cb00a0fFyodor Kupolov    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
9057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for the entire download list */
9257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download";
9357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for an individual download */
9457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download";
9557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher used to recognize URIs sent by applications */
9757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads belonging to the calling UID */
993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS = 1;
1003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of an individual download belonging to the calling UID */
1013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS_ID = 2;
1023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads in the system */
1033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS = 3;
10457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher constant for the URI of an individual download */
1053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS_ID = 4;
1065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /** URI matcher constant for the URI of a download's request headers */
1073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int REQUEST_HEADERS_URI = 5;
1083ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori    /** URI matcher constant for the public URI returned by
1093ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     * {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file
1103ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     * is publicly accessible.
1113ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori     */
1123ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori    private static final int PUBLIC_DOWNLOAD_ID = 6;
11357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    static {
1143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
1153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
1163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS);
1173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
1183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
1193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
1213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
1223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
1244bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        // temporary, for backwards compatibility
1254bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS);
1264bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID);
1274bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads",
1284bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
1294bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                REQUEST_HEADERS_URI);
1303ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori        sURIMatcher.addURI("downloads",
1313ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#",
1323ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                PUBLIC_DOWNLOAD_ID);
13357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
13457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** Different base URIs that could be used to access an individual download */
1363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final Uri[] BASE_URIS = new Uri[] {
1373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.CONTENT_URI,
1383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
1393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    };
1403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final String[] sAppReadableColumnsArray = new String[] {
1427dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._ID,
1437dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_APP_DATA,
1447dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._DATA,
1457dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_MIME_TYPE,
1467dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_VISIBILITY,
1477dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_DESTINATION,
1487dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CONTROL,
1497dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_STATUS,
1507dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_LAST_MODIFICATION,
1517dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
1527dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
1537dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TOTAL_BYTES,
1547dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CURRENT_BYTES,
1557dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TITLE,
1560a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        Downloads.Impl.COLUMN_DESCRIPTION,
1570d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard        Downloads.Impl.COLUMN_URI,
15871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
159b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        Downloads.Impl.COLUMN_FILE_NAME_HINT,
160e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
161e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_DELETED,
1629b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        OpenableColumns.DISPLAY_NAME,
1639b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        OpenableColumns.SIZE,
1641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    };
1651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1669b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey    private static final HashSet<String> sAppReadableColumnsSet;
1679b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey    private static final HashMap<String, String> sColumnsMap;
1689b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
1691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    static {
1701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        sAppReadableColumnsSet = new HashSet<String>();
1711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        for (int i = 0; i < sAppReadableColumnsArray.length; ++i) {
1721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]);
1731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
1749b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
1759b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap = Maps.newHashMap();
1769b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap.put(OpenableColumns.DISPLAY_NAME,
1779b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                Downloads.Impl.COLUMN_TITLE + " AS " + OpenableColumns.DISPLAY_NAME);
1789b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey        sColumnsMap.put(OpenableColumns.SIZE,
1799b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + OpenableColumns.SIZE);
1801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
18101d0182d86db003b2da5b831cb26820093888d9aVasu Nori    private static final List<String> downloadManagerColumnsList =
18201d0182d86db003b2da5b831cb26820093888d9aVasu Nori            Arrays.asList(DownloadManager.UNDERLYING_COLUMNS);
1831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1843a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey    @VisibleForTesting
1853a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey    SystemFacade mSystemFacade;
186c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey
18757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** The database that lies underneath this content provider */
18857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private SQLiteOpenHelper mOpenHelper = null;
18957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
19091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    /** List of uids that can access the downloads */
19191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mSystemUid = -1;
19291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mDefContainerUid = -1;
19391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu
19457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
195e610c0502c00689411624c00c3f81497df93b202Steve Howard     * This class encapsulates a SQL where clause and its parameters.  It makes it possible for
196e610c0502c00689411624c00c3f81497df93b202Steve Howard     * shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)})
197e610c0502c00689411624c00c3f81497df93b202Steve Howard     * to return both pieces of information, and provides some utility logic to ease piece-by-piece
198e610c0502c00689411624c00c3f81497df93b202Steve Howard     * construction of selections.
199e610c0502c00689411624c00c3f81497df93b202Steve Howard     */
200e610c0502c00689411624c00c3f81497df93b202Steve Howard    private static class SqlSelection {
201e610c0502c00689411624c00c3f81497df93b202Steve Howard        public StringBuilder mWhereClause = new StringBuilder();
202e610c0502c00689411624c00c3f81497df93b202Steve Howard        public List<String> mParameters = new ArrayList<String>();
203e610c0502c00689411624c00c3f81497df93b202Steve Howard
204e610c0502c00689411624c00c3f81497df93b202Steve Howard        public <T> void appendClause(String newClause, final T... parameters) {
205e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (newClause == null || newClause.isEmpty()) {
206e610c0502c00689411624c00c3f81497df93b202Steve Howard                return;
207e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
208e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (mWhereClause.length() != 0) {
209e610c0502c00689411624c00c3f81497df93b202Steve Howard                mWhereClause.append(" AND ");
210e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
211e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append("(");
212e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(newClause);
213e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(")");
214e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (parameters != null) {
215e610c0502c00689411624c00c3f81497df93b202Steve Howard                for (Object parameter : parameters) {
216e610c0502c00689411624c00c3f81497df93b202Steve Howard                    mParameters.add(parameter.toString());
217e610c0502c00689411624c00c3f81497df93b202Steve Howard                }
218e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
219e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
220e610c0502c00689411624c00c3f81497df93b202Steve Howard
221e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String getSelection() {
222e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mWhereClause.toString();
223e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
224e610c0502c00689411624c00c3f81497df93b202Steve Howard
225e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String[] getParameters() {
226e610c0502c00689411624c00c3f81497df93b202Steve Howard            String[] array = new String[mParameters.size()];
227e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mParameters.toArray(array);
228e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
229e610c0502c00689411624c00c3f81497df93b202Steve Howard    }
230e610c0502c00689411624c00c3f81497df93b202Steve Howard
231e610c0502c00689411624c00c3f81497df93b202Steve Howard    /**
23257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Creates and updated database on demand when opening it.
23357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Helper class to create database the first time the provider is
23457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * initialized and upgrade it when a new version of the provider needs
23557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * an updated version of the database.
23657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
23757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private final class DatabaseHelper extends SQLiteOpenHelper {
23857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public DatabaseHelper(final Context context) {
23957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            super(context, DB_NAME, null, DB_VERSION);
240635e12dcdf24215246615c78c5de6b883cb00a0fFyodor Kupolov            setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
24157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
24257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
24357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
24457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Creates database the first time we try to open it.
24557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
24657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
24757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
24857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
2499b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "populating new database");
25057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
2515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            onUpgrade(db, 0, DB_VERSION);
25257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
25357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
25457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
25557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Updates the database format when a content provider is used
25657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * with a database that was created with a different format.
2575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         *
2585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Note: to support downgrades, creating a table should always drop it first if it already
2595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * exists.
26057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
26157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
2621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) {
2635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (oldV == 31) {
2645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // 31 and 100 are identical, just in different codelines. Upgrading from 31 is the
2655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // same as upgrading from 100.
2665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 100;
2675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV < 100) {
2685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // no logic to upgrade from these older version, just recreate the DB
2699b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV
2705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " to version " + newV + ", which will destroy all old data");
2715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV > newV) {
2735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // user must have downgraded software; we have no way to know how to downgrade the
2745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // DB, so just recreate it
2759b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.i(Constants.TAG, "Downgrading downloads database from version " + oldV
2765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " (current version is " + newV + "), destroying all old data");
2775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (int version = oldV + 1; version <= newV; version++) {
2815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                upgradeTo(db, version);
2825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
2865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Upgrade database from (version - 1) to version.
2875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
2885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void upgradeTo(SQLiteDatabase db, int version) {
2895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            switch (version) {
2905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 100:
2915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createDownloadsTable(db);
2925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 101:
2955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createHeadersTable(db);
2965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2980a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                case 102:
2990a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_PUBLIC_API,
3000a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
3010a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_ROAMING,
3020a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
3030a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES,
3040a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
3050a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    break;
3060a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
30771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                case 103:
30871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
30971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                              "INTEGER NOT NULL DEFAULT 1");
31071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    makeCacheDownloadsInvisible(db);
31171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    break;
31271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
313d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                case 104:
314d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT,
315d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                            "INTEGER NOT NULL DEFAULT 0");
316d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    break;
317d319729622da1893e895f2e35f41d01ecdca3705Steve Howard
31873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                case 105:
31973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    fillNullValues(db);
32073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    break;
32173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
322e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                case 106:
323e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, "TEXT");
324e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_DELETED,
325e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                            "BOOLEAN NOT NULL DEFAULT 0");
326e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    break;
327e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
3289d27069a5453574824860ad3db179599d044e7bdVasu Nori                case 107:
3299d27069a5453574824860ad3db179599d044e7bdVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT");
3309d27069a5453574824860ad3db179599d044e7bdVasu Nori                    break;
3319d27069a5453574824860ad3db179599d044e7bdVasu Nori
332a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                case 108:
333a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_METERED,
334a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                            "INTEGER NOT NULL DEFAULT 1");
335a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey                    break;
336a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey
337c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                case 109:
338c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_WRITE,
339c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                            "BOOLEAN NOT NULL DEFAULT 0");
340c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                    break;
341c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey
3423a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                case 110:
3433a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_FLAGS,
3443a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                            "INTEGER NOT NULL DEFAULT 0");
3453a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    break;
3463a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
3475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                default:
3485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalStateException("Don't know how to upgrade to " + version);
3495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
3505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
3515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
3525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
35373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * insert() now ensures these four columns are never null for new downloads, so this method
35473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * makes that true for existing columns, so that code can rely on this assumption.
35573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         */
35673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValues(SQLiteDatabase db) {
35773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            ContentValues values = new ContentValues();
35873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
35973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
36073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
36173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
36273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TITLE, "");
36373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
36473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_DESCRIPTION, "");
36573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
36673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
36773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
36873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValuesForColumn(SQLiteDatabase db, ContentValues values) {
36973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            String column = values.valueSet().iterator().next().getKey();
37073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            db.update(DB_TABLE, values, column + " is null", null);
37173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.clear();
37273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
37373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
37473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        /**
37571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         * Set all existing downloads to the cache partition to be invisible in the downloads UI.
37671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         */
37771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        private void makeCacheDownloadsInvisible(SQLiteDatabase db) {
37871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            ContentValues values = new ContentValues();
37971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false);
38071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            String cacheSelection = Downloads.Impl.COLUMN_DESTINATION
38171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    + " != " + Downloads.Impl.DESTINATION_EXTERNAL;
38271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            db.update(DB_TABLE, values, cacheSelection, null);
38371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
38471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
38571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        /**
3860a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * Add a column to a table using ALTER TABLE.
3870a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param dbTable name of the table
3880a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnName name of the column to add
3890a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnDefinition SQL for the column definition
3900a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         */
3910a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        private void addColumn(SQLiteDatabase db, String dbTable, String columnName,
3920a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                               String columnDefinition) {
3930a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            db.execSQL("ALTER TABLE " + dbTable + " ADD COLUMN " + columnName + " "
3940a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                       + columnDefinition);
3950a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
3960a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
3970a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        /**
3985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Creates the table that'll hold the download information.
3995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
4005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createDownloadsTable(SQLiteDatabase db) {
4015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            try {
4025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
4035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("CREATE TABLE " + DB_TABLE + "(" +
4045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
4055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_URI + " TEXT, " +
4065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, " +
4075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_APP_DATA + " TEXT, " +
4085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NO_INTEGRITY + " BOOLEAN, " +
4095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_FILE_NAME_HINT + " TEXT, " +
410f20af91262fecce05928167123c8d335b4cfd33dVasu Nori                        Constants.OTA_UPDATE + " BOOLEAN, " +
4115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._DATA + " TEXT, " +
4125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_MIME_TYPE + " TEXT, " +
4135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESTINATION + " INTEGER, " +
4145224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.NO_SYSTEM_FILES + " BOOLEAN, " +
4155224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_VISIBILITY + " INTEGER, " +
4165224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CONTROL + " INTEGER, " +
4175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_STATUS + " INTEGER, " +
41812f5dc46aaa8e28cabfbe25d55f0af68f24ab306Jeff Sharkey                        Downloads.Impl.COLUMN_FAILED_CONNECTIONS + " INTEGER, " +
4195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_LAST_MODIFICATION + " BIGINT, " +
4205224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + " TEXT, " +
4215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS + " TEXT, " +
4225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + " TEXT, " +
4235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_COOKIE_DATA + " TEXT, " +
4245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_USER_AGENT + " TEXT, " +
4255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_REFERER + " TEXT, " +
4265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TOTAL_BYTES + " INTEGER, " +
4275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CURRENT_BYTES + " INTEGER, " +
4285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.ETAG + " TEXT, " +
4295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.UID + " INTEGER, " +
4305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_OTHER_UID + " INTEGER, " +
4315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TITLE + " TEXT, " +
4325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESCRIPTION + " TEXT, " +
433495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        Downloads.Impl.COLUMN_MEDIA_SCANNED + " BOOLEAN);");
4345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } catch (SQLException ex) {
4359b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.e(Constants.TAG, "couldn't create table in downloads database");
4365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                throw ex;
4375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
4385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
4395224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
4405224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createHeadersTable(SQLiteDatabase db) {
4415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("DROP TABLE IF EXISTS " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE);
4425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("CREATE TABLE " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + "(" +
4435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       "id INTEGER PRIMARY KEY AUTOINCREMENT," +
4445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL," +
4455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL," +
4465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL" +
4475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       ");");
44857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
44957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
45057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
45157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
45257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Initializes the content provider when it is created.
45357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
45457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
45557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public boolean onCreate() {
4566d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        if (mSystemFacade == null) {
457af28400b74de05862b470412a5c92f68e99f59f8Steve Howard            mSystemFacade = new RealSystemFacade(getContext());
4586d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        }
4596d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
46057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        mOpenHelper = new DatabaseHelper(getContext());
46191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the system uid
46291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        mSystemUid = Process.SYSTEM_UID;
46391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the default container uid. Package name hardcoded
46491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // for now.
46591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        ApplicationInfo appInfo = null;
46691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        try {
46791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            appInfo = getContext().getPackageManager().
46891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu                    getApplicationInfo("com.android.defcontainer", 0);
46991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        } catch (NameNotFoundException e) {
4709b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.wtf(Constants.TAG, "Could not get ApplicationInfo for com.android.defconatiner", e);
47191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
47291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        if (appInfo != null) {
47391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            mDefContainerUid = appInfo.uid;
47491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
4757c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey
4767c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        // Grant access permissions for all known downloads to the owning apps
4777c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4787c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        final Cursor cursor = db.query(DB_TABLE, new String[] {
4797c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey                Downloads.Impl._ID, Constants.UID }, null, null, null, null, null);
4802ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        final ArrayList<Long> idsToDelete = new ArrayList<>();
4817c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        try {
4827c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            while (cursor.moveToNext()) {
4832ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                final long downloadId = cursor.getLong(0);
4842ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                final int uid = cursor.getInt(1);
4852ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                final String ownerPackage = getPackageForUid(uid);
4862ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                if (ownerPackage == null) {
4872ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                    idsToDelete.add(downloadId);
4882ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                } else {
4892ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                    grantAllDownloadsPermission(ownerPackage, downloadId);
4902ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                }
4917c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            }
4927c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        } finally {
4937c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            cursor.close();
4947c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        }
4952ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        if (idsToDelete.size() > 0) {
4962ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            Log.i(Constants.TAG,
4972ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla                    "Deleting downloads with ids " + idsToDelete + " as owner package is missing");
4982ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            deleteDownloadsWithIds(idsToDelete);
4992ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        }
50057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return true;
50157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
50257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5032ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla    private void deleteDownloadsWithIds(ArrayList<Long> downloadIds) {
5042ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        final int N = downloadIds.size();
5052ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        if (N == 0) {
5062ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            return;
5072ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        }
5082ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        final StringBuilder queryBuilder = new StringBuilder(Downloads.Impl._ID + " in (");
5092ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        for (int i = 0; i < N; i++) {
5102ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            queryBuilder.append(downloadIds.get(i));
5112ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            queryBuilder.append((i == N - 1) ? ")" : ",");
5122ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        }
5132ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, queryBuilder.toString(), null);
5142ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla    }
5152ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla
51657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
51757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Returns the content-provider-style MIME types of the various
51857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * types accessible through this content provider.
51957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
52057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
52157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public String getType(final Uri uri) {
52257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
52357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
5249b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case MY_DOWNLOADS:
5259b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case ALL_DOWNLOADS: {
52657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_LIST_TYPE;
52757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
5289b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            case MY_DOWNLOADS_ID:
529c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey            case ALL_DOWNLOADS_ID:
5303ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori            case PUBLIC_DOWNLOAD_ID: {
5313ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                // return the mimetype of this id from the database
5323ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                final String id = getDownloadIdFromUri(uri);
5333ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
534c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                final String mimeType = DatabaseUtils.stringForQuery(db,
5353ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE +
5363ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        " WHERE " + Downloads.Impl._ID + " = ?",
5373ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                        new String[]{id});
538c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                if (TextUtils.isEmpty(mimeType)) {
539c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                    return DOWNLOAD_TYPE;
540c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                } else {
541c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                    return mimeType;
542c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey                }
5433ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori            }
54457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            default: {
54557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                if (Constants.LOGV) {
5469b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                    Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri);
54757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                }
54857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new IllegalArgumentException("Unknown URI: " + uri);
54957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
55057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
55157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
55257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
55357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
55457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Inserts a row in the database
55557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
55657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
55757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public Uri insert(final Uri uri, final ContentValues values) {
558b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        checkInsertPermissions(values);
55957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
56057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5613d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        // note we disallow inserting into ALL_DOWNLOADS
5623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        int match = sURIMatcher.match(uri);
5633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match != MY_DOWNLOADS) {
5649b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri);
56557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
56657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
56757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
568b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some of the input values as it
5691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues = new ContentValues();
5707dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_URI, values, filteredValues);
5717dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
5727dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues);
5737dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues);
5747dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues);
57571aab521efba9b28779541440c797220ec98ac97Steve Howard        copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues);
576b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
57771aab521efba9b28779541440c797220ec98ac97Steve Howard        boolean isPublicApi =
57871aab521efba9b28779541440c797220ec98ac97Steve Howard                values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE;
57971aab521efba9b28779541440c797220ec98ac97Steve Howard
580b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the destination column
5817dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION);
582ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (dest != null) {
583dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey            if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
5841fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    != PackageManager.PERMISSION_GRANTED
585b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                    && (dest == Downloads.Impl.DESTINATION_CACHE_PARTITION
586d635ac295850ba23d528c02b1e5c6eb44b64b22bJeff Sharkey                            || dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING)) {
5879aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                throw new SecurityException("setting destination to : " + dest +
5889aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori                        " not allowed, unless PERMISSION_ACCESS_ADVANCED is granted");
5891fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
59071aab521efba9b28779541440c797220ec98ac97Steve Howard            // for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically
59171aab521efba9b28779541440c797220ec98ac97Steve Howard            // switch to non-purgeable download
59271aab521efba9b28779541440c797220ec98ac97Steve Howard            boolean hasNonPurgeablePermission =
593dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey                    getContext().checkCallingOrSelfPermission(
59471aab521efba9b28779541440c797220ec98ac97Steve Howard                            Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE)
59571aab521efba9b28779541440c797220ec98ac97Steve Howard                            == PackageManager.PERMISSION_GRANTED;
59671aab521efba9b28779541440c797220ec98ac97Steve Howard            if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
59771aab521efba9b28779541440c797220ec98ac97Steve Howard                    && hasNonPurgeablePermission) {
59871aab521efba9b28779541440c797220ec98ac97Steve Howard                dest = Downloads.Impl.DESTINATION_CACHE_PARTITION;
59971aab521efba9b28779541440c797220ec98ac97Steve Howard            }
6006d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            if (dest == Downloads.Impl.DESTINATION_FILE_URI) {
601b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                checkFileUriDestination(values);
602ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
603ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            } else if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
604ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                getContext().enforceCallingOrSelfPermission(
605ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
606ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        "No permission to write");
607ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
608ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
609b8bc2a76ddaa0c59d681913dba10fd7ec0a2a22bJeff Sharkey                if (appOps.noteProxyOp(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
610ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                        getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
611ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    throw new SecurityException("No permission to write");
612ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                }
6136d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            }
6147dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);
615ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        }
616b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
617b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the visibility column
6187dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY);
619ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (vis == null) {
6207dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
6217dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
6227dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
623ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru            } else {
6247dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
6257dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_HIDDEN);
6261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
627ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        } else {
6287dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis);
62957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
630b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy the control column as is
6317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
632b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
633b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        /*
634b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori         * requests coming from
6359b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori         * DownloadManager.addCompletedDownload(String, String, String,
6369b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori         * boolean, String, String, long) need special treatment
637b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori         */
638b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
639b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
640b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            // these requests always are marked as 'completed'
641b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);
642b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES,
643b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                    values.getAsLong(Downloads.Impl.COLUMN_TOTAL_BYTES));
644b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
645b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            copyInteger(Downloads.Impl.COLUMN_MEDIA_SCANNED, values, filteredValues);
646b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            copyString(Downloads.Impl._DATA, values, filteredValues);
647c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            copyBoolean(Downloads.Impl.COLUMN_ALLOW_WRITE, values, filteredValues);
648b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        } else {
649b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
650b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
651b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
652b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        }
653b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
654b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // set lastupdate to current time
6552c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori        long lastMod = mSystemFacade.currentTimeMillis();
6562c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori        filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, lastMod);
6570a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
658b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // use packagename of the caller to set the notification columns
6597dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
6607dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
6610a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (pckg != null && (clazz != null || isPublicApi)) {
6621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            int uid = Binder.getCallingUid();
6631fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            try {
6640a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) {
6657dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg);
6660a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    if (clazz != null) {
6670a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                        filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz);
6680a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    }
6691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
6701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } catch (PackageManager.NameNotFoundException ex) {
6711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                /* ignored for now */
67257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
67357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
674b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
675b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some more columns as is
6767dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues);
6777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues);
6787dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues);
6797dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues);
680b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
681b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // UID, PID columns
682dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
6831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                == PackageManager.PERMISSION_GRANTED) {
6847dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues);
68557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
6861fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        filteredValues.put(Constants.UID, Binder.getCallingUid());
6871fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingUid() == 0) {
6881fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            copyInteger(Constants.UID, values, filteredValues);
68957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
690b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
691b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // copy some more columns as is
692a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, "");
693a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, "");
69457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
695b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // is_visible_in_downloads_ui column
69671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) {
69771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues);
69871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        } else {
69971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            // by default, make external downloads visible in the UI
70071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL);
70171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal);
70271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
70371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
704b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // public api requests and networktypes/roaming columns
7050a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (isPublicApi) {
7060a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);
7070a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);
708a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey            copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues);
7093a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey            copyInteger(Downloads.Impl.COLUMN_FLAGS, values, filteredValues);
7100a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
7110a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
7121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Constants.LOGVV) {
7139b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "initiating download with UID "
7141fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    + filteredValues.getAsInteger(Constants.UID));
7157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) {
7169b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "other UID " +
7177dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID));
7181fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
71957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
72057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        long rowID = db.insert(DB_TABLE, null, filteredValues);
7223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (rowID == -1) {
7239b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.d(Constants.TAG, "couldn't insert into downloads database");
7243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return null;
72557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
72657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        insertRequestHeaders(db, rowID, values);
7282ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla
7292ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        final String callingPackage = getPackageForUid(Binder.getCallingUid());
7302ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        if (callingPackage == null) {
7312ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            Log.e(Constants.TAG, "Package does not exist for calling uid");
7322ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            return null;
7332ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        }
7342ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        grantAllDownloadsPermission(callingPackage, rowID);
7353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
7361225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey
7373a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        final long token = Binder.clearCallingIdentity();
7383a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        try {
7393a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey            Helpers.scheduleJob(getContext(), rowID);
7403a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        } finally {
7413a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey            Binder.restoreCallingIdentity(token);
7423a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        }
7431225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey
744053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey        if (values.getAsInteger(COLUMN_DESTINATION) == DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD
745053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey                && values.getAsInteger(COLUMN_MEDIA_SCANNED) == 0) {
746053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey            DownloadScanner.requestScanBlocking(getContext(), rowID, values.getAsString(_DATA),
747053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey                    values.getAsString(COLUMN_MIME_TYPE));
748053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey        }
749053674aa4d0ecd7e37dadb8ee37a9cafbdf2f16cJeff Sharkey
7503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
75157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
75257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7532ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla    private String getPackageForUid(int uid) {
7542ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
7552ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        if (packages == null || packages.length == 0) {
7562ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla            return null;
7572ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        }
7582ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        // For permission related purposes, any package belonging to the given uid should work.
7592ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        return packages[0];
7602ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla    }
7612ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla
76257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
763b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Check that the file URI provided for DESTINATION_FILE_URI is valid.
764b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
765b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkFileUriDestination(ContentValues values) {
766b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String fileUri = values.getAsString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
767b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (fileUri == null) {
768b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException(
769b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    "DESTINATION_FILE_URI must include a file URI under COLUMN_FILE_NAME_HINT");
770b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
771b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Uri uri = Uri.parse(fileUri);
7725d81e2447ed77860afecd71583e137178c2c6807Steve Howard        String scheme = uri.getScheme();
7735d81e2447ed77860afecd71583e137178c2c6807Steve Howard        if (scheme == null || !scheme.equals("file")) {
774b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException("Not a file URI: " + uri);
775b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
776d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey        final String path = uri.getPath();
777b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        if (path == null) {
778b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard            throw new IllegalArgumentException("Invalid file URI: " + uri);
779b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        }
780ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
7818c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        final File file;
7828c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        try {
7838c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey            file = new File(path).getCanonicalFile();
7848c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        } catch (IOException e) {
7858c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey            throw new SecurityException(e);
7868c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        }
7878c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey
788ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        if (Helpers.isFilenameValidInExternalPackage(getContext(), file, getCallingPackage())) {
789ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            // No permissions required for paths belonging to calling package
790ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            return;
791ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        } else if (Helpers.isFilenameValidInExternal(getContext(), file)) {
792ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            // Otherwise we require write permission
793ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            getContext().enforceCallingOrSelfPermission(
794ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
795ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    "No permission to write to " + file);
796ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
797ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            final AppOpsManager appOps = getContext().getSystemService(AppOpsManager.class);
798b8bc2a76ddaa0c59d681913dba10fd7ec0a2a22bJeff Sharkey            if (appOps.noteProxyOp(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
799ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                    getCallingPackage()) != AppOpsManager.MODE_ALLOWED) {
800ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey                throw new SecurityException("No permission to write to " + file);
801d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey            }
802ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey
803ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey        } else {
804ed30deae5fe5b9de142b44933001c9b098c47712Jeff Sharkey            throw new SecurityException("Unsupported path " + file);
805b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
806b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
807b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
808b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
809b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to
810b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * constraints in the rest of the code. Apps without that may still access this provider through
811b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * the public API, but additional restrictions are imposed. We check those restrictions here.
812b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     *
813b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @param values ContentValues provided to insert()
814b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @throws SecurityException if the caller has insufficient permissions
815b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
816b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkInsertPermissions(ContentValues values) {
817b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS)
818b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                == PackageManager.PERMISSION_GRANTED) {
819b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            return;
820b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
821b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
822b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
823b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                "INTERNET permission is required to use the download manager");
824b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
825b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // ensure the request fits within the bounds of a public API request
826b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // first copy so we can remove values
827b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values = new ContentValues(values);
828b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
829b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // check columns whose values are restricted
830b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_IS_PUBLIC_API, Boolean.TRUE);
831b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori
832b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        // validate the destination column
833b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
834b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
835b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            /* this row is inserted by
8369b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori             * DownloadManager.addCompletedDownload(String, String, String,
8379b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori             * boolean, String, String, long)
838b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori             */
839b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl.COLUMN_TOTAL_BYTES);
840b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl._DATA);
841b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori            values.remove(Downloads.Impl.COLUMN_STATUS);
842b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori        }
843b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION,
844b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE,
845b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_FILE_URI,
846b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori                Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
8479da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard
8489da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION)
8499da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                == PackageManager.PERMISSION_GRANTED) {
8509da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
85151cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_HIDDEN,
85251cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE,
85351cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED,
85451cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
8559da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        } else {
8569da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
85751cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE,
85851cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED,
85951cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey                    Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
8609da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        }
861b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
862b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // remove the rest of the columns that are allowed (with any value)
863b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_URI);
864b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_TITLE);
865b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_DESCRIPTION);
866b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_MIME_TYPE);
867b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_FILE_NAME_HINT); // checked later in insert()
868b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert()
869b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
870b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
871a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey        values.remove(Downloads.Impl.COLUMN_ALLOW_METERED);
8723a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        values.remove(Downloads.Impl.COLUMN_FLAGS);
87371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
8743002e1870d4ad76c260b4729a8d86212c8db3e78Vasu Nori        values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED);
875c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        values.remove(Downloads.Impl.COLUMN_ALLOW_WRITE);
876e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator();
877e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        while (iterator.hasNext()) {
878e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            String key = iterator.next().getKey();
879e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
880e61798da80558450f580ed948d0d469bd6423d8eSteve Howard                iterator.remove();
881e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            }
882e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        }
883b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
884b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // any extra columns are extraneous and disallowed
885b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (values.size() > 0) {
886b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            StringBuilder error = new StringBuilder("Invalid columns in request: ");
887b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            boolean first = true;
888b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            for (Map.Entry<String, Object> entry : values.valueSet()) {
889b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                if (!first) {
890b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    error.append(", ");
891b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                }
892b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                error.append(entry.getKey());
893b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
894b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException(error.toString());
895b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
896b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
897b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
898b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
899b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Remove column from values, and throw a SecurityException if the value isn't within the
900b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * specified allowedValues.
901b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
902b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void enforceAllowedValues(ContentValues values, String column,
903b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            Object... allowedValues) {
904b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Object value = values.get(column);
905b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(column);
906b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        for (Object allowedValue : allowedValues) {
907b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value == null && allowedValue == null) {
908b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
909b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
910b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value != null && value.equals(allowedValue)) {
911b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
912b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
913b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
914b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        throw new SecurityException("Invalid value for " + column + ": " + value);
915b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
916b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
9172e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey    private Cursor queryCleared(Uri uri, String[] projection, String selection,
9182e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            String[] selectionArgs, String sort) {
9192e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        final long token = Binder.clearCallingIdentity();
9202e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        try {
9212e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            return query(uri, projection, selection, selectionArgs, sort);
9222e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        } finally {
9232e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey            Binder.restoreCallingIdentity(token);
9242e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        }
9252e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey    }
9262e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey
927b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
92857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Starts a database query
92957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
93057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
9311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    public Cursor query(final Uri uri, String[] projection,
93257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String selection, final String[] selectionArgs,
93357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String sort) {
9341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
93557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
93657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
93757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
9383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == -1) {
9393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (Constants.LOGV) {
9409b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "querying unknown URI: " + uri);
94157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
9423d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            throw new IllegalArgumentException("Unknown URI: " + uri);
9433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == REQUEST_HEADERS_URI) {
9463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (projection != null || selection != null || sort != null) {
9473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new UnsupportedOperationException("Request header queries do not support "
9483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                                                        + "projections, selections or sorting");
94957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
9503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return queryRequestHeaders(db, uri);
9513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9523d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
953e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection fullSelection = getWhereClause(uri, selection, selectionArgs, match);
95457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        if (shouldRestrictVisibility()) {
9561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (projection == null) {
9579b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                projection = sAppReadableColumnsArray.clone();
9581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } else {
95901d0182d86db003b2da5b831cb26820093888d9aVasu Nori                // check the validity of the columns in projection
9601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                for (int i = 0; i < projection.length; ++i) {
96101d0182d86db003b2da5b831cb26820093888d9aVasu Nori                    if (!sAppReadableColumnsSet.contains(projection[i]) &&
96201d0182d86db003b2da5b831cb26820093888d9aVasu Nori                            !downloadManagerColumnsList.contains(projection[i])) {
9631fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                        throw new IllegalArgumentException(
9641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                                "column " + projection[i] + " is not allowed in queries");
9651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    }
9661fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
9671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
9689b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey
9699b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            for (int i = 0; i < projection.length; i++) {
9709b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                final String newColumn = sColumnsMap.get(projection[i]);
9719b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                if (newColumn != null) {
9729b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                    projection[i] = newColumn;
9739b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey                }
9749b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey            }
97557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
97657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
97757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
9783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseQueryInfo(projection, selection, selectionArgs, sort, db);
97957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
98057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
981b759707b80987d0cb4ad2a3a78c11702a45a36c2Ben Lin        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
982b759707b80987d0cb4ad2a3a78c11702a45a36c2Ben Lin        builder.setTables(DB_TABLE);
983b759707b80987d0cb4ad2a3a78c11702a45a36c2Ben Lin        builder.setStrict(true);
984b759707b80987d0cb4ad2a3a78c11702a45a36c2Ben Lin        Cursor ret = builder.query(db, projection, fullSelection.getSelection(),
985e610c0502c00689411624c00c3f81497df93b202Steve Howard                fullSelection.getParameters(), null, null, sort);
98657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
98757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret != null) {
98857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            ret.setNotificationUri(getContext().getContentResolver(), uri);
98957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
9909b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG,
99157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                        "created cursor " + ret + " on behalf of " + Binder.getCallingPid());
99257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
99357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        } else {
99457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGV) {
9959b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.v(Constants.TAG, "query failed in downloads database");
99657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
99757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
99857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
99957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
100057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
100157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
10023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseQueryInfo(String[] projection, final String selection,
10033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            final String[] selectionArgs, final String sort, SQLiteDatabase db) {
10043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        java.lang.StringBuilder sb = new java.lang.StringBuilder();
10053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("starting query, database is ");
10063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (db != null) {
10073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("not ");
10083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("null; ");
10103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (projection == null) {
10113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is null; ");
10123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (projection.length == 0) {
10133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is empty; ");
10143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < projection.length; ++i) {
10163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("projection[");
10173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
10183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
10193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(projection[i]);
10203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
10213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("selection is ");
10243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(selection);
10253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("; ");
10263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (selectionArgs == null) {
10273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is null; ");
10283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (selectionArgs.length == 0) {
10293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is empty; ");
10303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < selectionArgs.length; ++i) {
10323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("selectionArgs[");
10333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
10343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
10353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(selectionArgs[i]);
10363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
10373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("sort is ");
10403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(sort);
10413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(".");
10429b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker        Log.v(Constants.TAG, sb.toString());
10433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
10443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
10455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private String getDownloadIdFromUri(final Uri uri) {
10465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return uri.getPathSegments().get(1);
10475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Insert request headers for a download into the DB.
10515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void insertRequestHeaders(SQLiteDatabase db, long downloadId, ContentValues values) {
10535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        ContentValues rowValues = new ContentValues();
10545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID, downloadId);
10555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        for (Map.Entry<String, Object> entry : values.valueSet()) {
10565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            String key = entry.getKey();
10575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
10585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String headerLine = entry.getValue().toString();
10595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                if (!headerLine.contains(":")) {
10605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalArgumentException("Invalid HTTP header line: " + headerLine);
10615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                }
10625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String[] parts = headerLine.split(":", 2);
10635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_HEADER, parts[0].trim());
10645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_VALUE, parts[1].trim());
10655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.insert(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, null, rowValues);
10665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
10675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
10685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Handle a query for the custom request headers registered for a download.
10725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) {
10745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
10755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       + getDownloadIdFromUri(uri);
10765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
10775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                            Downloads.Impl.RequestHeaders.COLUMN_VALUE};
107853356ad554f53a093434161cb494b8e818c294fbSteve Howard        return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
107953356ad554f53a093434161cb494b8e818c294fbSteve Howard                        null, null, null, null);
10805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
10835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Delete request headers for downloads matching the given query.
10845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
10855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) {
10865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl._ID};
1087b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null);
10885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        try {
10895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
10905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                long id = cursor.getLong(0);
10915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id;
10925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null);
10935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
10945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        } finally {
10955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            cursor.close();
10965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
10975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
10985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
10995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
11003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @return true if we should restrict the columns readable by this caller
11015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
11025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private boolean shouldRestrictVisibility() {
11035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        int callingUid = Binder.getCallingUid();
11045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return Binder.getCallingPid() != Process.myPid() &&
11055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mSystemUid &&
110612c7bd488afe3a611d665e364e361f04f4a8c8d8Jeff Brown                callingUid != mDefContainerUid;
11075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
11085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
110957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
111057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Updates a row in the database
111157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
111257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
111357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int update(final Uri uri, final ContentValues values,
111457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String where, final String[] whereArgs) {
111550522c738bf68d2784ce4f52dd34188a491065b8Jeff Sharkey        if (shouldRestrictVisibility()) {
111650522c738bf68d2784ce4f52dd34188a491065b8Jeff Sharkey            Helpers.validateSelection(where, sAppReadableColumnsSet);
111750522c738bf68d2784ce4f52dd34188a491065b8Jeff Sharkey        }
11181fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
111916ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey        final Context context = getContext();
112016ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey        final ContentResolver resolver = context.getContentResolver();
112116ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey
112216ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
112357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
112457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
11253a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        boolean updateSchedule = false;
112616ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey        boolean isCompleting = false;
1127e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
11281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues;
11291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingPid() != Process.myPid()) {
11301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = new ContentValues();
11317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
11327dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues);
11337dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL);
11341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (i != null) {
11357dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i);
11363a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                updateSchedule = true;
11371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
1138e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
11397dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
11407dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues);
1141e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, values, filteredValues);
11427dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues);
1143e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyInteger(Downloads.Impl.COLUMN_DELETED, values, filteredValues);
11441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        } else {
11451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = values;
1146a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            String filename = values.getAsString(Downloads.Impl._DATA);
1147a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            if (filename != null) {
1148a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                Cursor c = null;
1149a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                try {
1150a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    c = query(uri, new String[]
1151a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                            { Downloads.Impl.COLUMN_TITLE }, null, null, null);
1152a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    if (!c.moveToFirst() || c.getString(0).isEmpty()) {
1153a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        values.put(Downloads.Impl.COLUMN_TITLE, new File(filename).getName());
1154a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    }
1155a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } finally {
1156a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    IoUtils.closeQuietly(c);
1157a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                }
1158a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            }
115971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
116071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS);
116171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING;
1162d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            boolean isUserBypassingSizeLimit =
1163d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                values.containsKey(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
1164d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            if (isRestart || isUserBypassingSizeLimit) {
11653a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                updateSchedule = true;
116671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            }
116716ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey            isCompleting = status != null && Downloads.Impl.isStatusCompleted(status);
116857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
11693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
117057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
117157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
11723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
11733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
11743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
11753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
11763a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                if (filteredValues.size() == 0) {
11771fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    count = 0;
11783a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    break;
11793a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                }
11803a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
11813a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
11823a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
11833a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        selection.getParameters());
118416ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                if (updateSchedule || isCompleting) {
11853a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    final long token = Binder.clearCallingIdentity();
118616ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                    try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
118716ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            selection.getParameters(), null, null, null)) {
118816ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver,
118916ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                                cursor);
119016ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        final DownloadInfo info = new DownloadInfo(context);
119116ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        while (cursor.moveToNext()) {
119216ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            reader.updateFromDatabase(info);
119316ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            if (updateSchedule) {
119416ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                                Helpers.scheduleJob(context, info);
119516ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            }
119616ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            if (isCompleting) {
119716ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                                info.sendIntentIfRequested();
11983a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                            }
11993a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        }
12003a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    } finally {
12013a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        Binder.restoreCallingIdentity(token);
12023a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                    }
12031fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
120457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
12053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
12063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
12079b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri);
120857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot update URI: " + uri);
120957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
12103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
12113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
121257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
121357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
121457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
12153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /**
12163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * Notify of a change through both URIs (/my_downloads and /all_downloads)
12173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uri either URI for the changed download(s)
12183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uriMatch the match ID from {@link #sURIMatcher}
12193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     */
12203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void notifyContentChanged(final Uri uri, int uriMatch) {
12213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Long downloadId = null;
12223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
12233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            downloadId = Long.parseLong(getDownloadIdFromUri(uri));
12243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
12253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        for (Uri uriToNotify : BASE_URIS) {
12263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (downloadId != null) {
12273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                uriToNotify = ContentUris.withAppendedId(uriToNotify, downloadId);
12283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
12293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            getContext().getContentResolver().notifyChange(uriToNotify, null);
12303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
12313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
12323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1233e610c0502c00689411624c00c3f81497df93b202Steve Howard    private SqlSelection getWhereClause(final Uri uri, final String where, final String[] whereArgs,
1234e610c0502c00689411624c00c3f81497df93b202Steve Howard            int uriMatch) {
1235e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection selection = new SqlSelection();
1236e610c0502c00689411624c00c3f81497df93b202Steve Howard        selection.appendClause(where, whereArgs);
12373ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID ||
12383ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori                uriMatch == PUBLIC_DOWNLOAD_ID) {
1239e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri));
1240b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
1241e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root        if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID)
1242dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey                && getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS_ALL)
1243e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root                != PackageManager.PERMISSION_GRANTED) {
1244e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(
1245e610c0502c00689411624c00c3f81497df93b202Steve Howard                    Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?",
1246bff4fe9b858d95e984298d4863a5199f1ee2c54eJeff Sharkey                    Binder.getCallingUid(), Binder.getCallingUid());
1247b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
1248e610c0502c00689411624c00c3f81497df93b202Steve Howard        return selection;
1249b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
1250b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
125157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
125257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Deletes a row in the database
125357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
125457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
1255e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey    public int delete(final Uri uri, final String where, final String[] whereArgs) {
12561f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey        if (shouldRestrictVisibility()) {
12571f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey            Helpers.validateSelection(where, sAppReadableColumnsSet);
12581f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey        }
12591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1260efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey        final Context context = getContext();
1261efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey        final ContentResolver resolver = context.getContentResolver();
1262efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey        final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
1263efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey
12643a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
126557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
126657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
126757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
12683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
12693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
12703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
12713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
12723a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                final SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
1273e610c0502c00689411624c00c3f81497df93b202Steve Howard                deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
1274afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey
1275efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                try (Cursor cursor = db.query(DB_TABLE, null, selection.getSelection(),
1276efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        selection.getParameters(), null, null, null)) {
1277efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                    final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
1278efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                    final DownloadInfo info = new DownloadInfo(context);
1279afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                    while (cursor.moveToNext()) {
1280efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        reader.updateFromDatabase(info);
1281efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        scheduler.cancel((int) info.mId);
12823a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
1283e2c5d91b95a39a1e5028581bd72bd0e0ea907cafJeff Sharkey                        revokeAllDownloadsPermission(info.mId);
1284efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        DownloadStorageProvider.onDownloadProviderDelete(getContext(), info.mId);
1285e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey
1286efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        final String path = info.mFileName;
1287e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        if (!TextUtils.isEmpty(path)) {
12888c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                            try {
12898c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                final File file = new File(path).getCanonicalFile();
12908c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                if (Helpers.isFilenameValid(getContext(), file)) {
12918c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                    Log.v(Constants.TAG,
12928c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                            "Deleting " + file + " via provider delete");
12938c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                    file.delete();
12948c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                                }
12958c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey                            } catch (IOException ignored) {
1296e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                            }
1297e3960ea97d5ddee80e5237796d577892e42a28cbJeff Sharkey                        }
12983a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey
1299efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey                        final String mediaUri = info.mMediaProviderUri;
13003a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        if (!TextUtils.isEmpty(mediaUri)) {
130101dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                            final long token = Binder.clearCallingIdentity();
130201dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                            try {
130301dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                                getContext().getContentResolver().delete(Uri.parse(mediaUri), null,
130401dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                                        null);
1305d635ac295850ba23d528c02b1e5c6eb44b64b22bJeff Sharkey                            } catch (Exception e) {
1306d635ac295850ba23d528c02b1e5c6eb44b64b22bJeff Sharkey                                Log.w(Constants.TAG, "Failed to delete media entry: " + e);
130701dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                            } finally {
130801dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                                Binder.restoreCallingIdentity(token);
130901dee865054aedb9aa7e90a8ef2c556e19af95e1Jeff Sharkey                            }
13103a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        }
1311efb1ac6b49692e62fde6830c3d20953c8632d2baJeff Sharkey
131216ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        // If the download wasn't completed yet, we're
131316ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        // effectively completing it now, and we need to send
131416ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        // any requested broadcasts
131516ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        if (!Downloads.Impl.isStatusCompleted(info.mStatus)) {
131616ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                            info.sendIntentIfRequested();
131716ee9158a395f3f67be12062d7f870f76e2677a5Jeff Sharkey                        }
1318afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                    }
1319afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey                }
1320afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey
1321e610c0502c00689411624c00c3f81497df93b202Steve Howard                count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters());
132257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
13233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
13243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
13259b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker                Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri);
132657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot delete URI: " + uri);
132757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
13283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
13293b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey        final long token = Binder.clearCallingIdentity();
13303b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey        try {
13313b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey            Helpers.getDownloadNotifier(getContext()).update();
13323b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey        } finally {
13333b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey            Binder.restoreCallingIdentity(token);
13343b7e099588a2697305fd52c342f404a03ec9a9abJeff Sharkey        }
133557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
133657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
133771aab521efba9b28779541440c797220ec98ac97Steve Howard
133857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
133957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Remotely opens a file
134057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
134157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
1342c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey    public ParcelFileDescriptor openFile(final Uri uri, String mode) throws FileNotFoundException {
134357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
13443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseOpenFileInfo(uri, mode);
134557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
13461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13477c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        // Perform normal query to enforce caller identity access before
13487c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        // clearing it to reach internal-only columns
13497c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        final Cursor probeCursor = query(uri, new String[] {
13507c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey                Downloads.Impl._DATA }, null, null, null);
13517c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        try {
13527c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            if ((probeCursor == null) || (probeCursor.getCount() == 0)) {
13537c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey                throw new FileNotFoundException(
13547c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey                        "No file found for " + uri + " as UID " + Binder.getCallingUid());
13557c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            }
13567c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        } finally {
13577c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey            IoUtils.closeQuietly(probeCursor);
13587c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        }
13597c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey
13602e16979a9133ef83f6e513eaa4b84de40350a2ddJeff Sharkey        final Cursor cursor = queryCleared(uri, new String[] {
1361495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                Downloads.Impl._DATA, Downloads.Impl.COLUMN_STATUS,
1362495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.COLUMN_MEDIA_SCANNED }, null,
1363495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                null, null);
1364495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final String path;
1365495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final boolean shouldScan;
13663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        try {
13673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            int count = (cursor != null) ? cursor.getCount() : 0;
13683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (count != 1) {
13693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                // If there is not exactly one result, throw an appropriate exception.
13703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (count == 0) {
13713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    throw new FileNotFoundException("No entry for " + uri);
13723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
13733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new FileNotFoundException("Multiple items at " + uri);
13741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
13753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1376495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            if (cursor.moveToFirst()) {
1377495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int status = cursor.getInt(1);
1378495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int destination = cursor.getInt(2);
1379495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                final int mediaScanned = cursor.getInt(3);
1380495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey
1381495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                path = cursor.getString(0);
1382495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                shouldScan = Downloads.Impl.isStatusSuccess(status) && (
1383495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        destination == Downloads.Impl.DESTINATION_EXTERNAL
1384495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        || destination == Downloads.Impl.DESTINATION_FILE_URI
1385495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        || destination == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
1386495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        && mediaScanned != 2;
1387495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            } else {
1388495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                throw new FileNotFoundException("Failed moveToFirst");
1389495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            }
13903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } finally {
1391c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            IoUtils.closeQuietly(cursor);
13921fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
13931fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13941fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (path == null) {
13951fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("No filename found.");
13961fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
13973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
13988c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        final File file;
13998c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        try {
14008c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey            file = new File(path).getCanonicalFile();
14018c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        } catch (IOException e) {
14028c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey            throw new FileNotFoundException(e.getMessage());
14038c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey        }
14048c088f73a53813869e68a5c4671c09e8732da6d3Jeff Sharkey
1405dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        if (!Helpers.isFilenameValid(getContext(), file)) {
1406a4af46faac3ed68540d634e25f311cb7e071e6a5Jeff Sharkey            throw new FileNotFoundException("Invalid file: " + file);
1407dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey        }
1408dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey
1409495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        final int pfdMode = ParcelFileDescriptor.parseMode(mode);
1410495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey        if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
1411495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey            return ParcelFileDescriptor.open(file, pfdMode);
1412c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        } else {
1413c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            try {
1414c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                // When finished writing, update size and timestamp
14153a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                return ParcelFileDescriptor.open(file, pfdMode, Helpers.getAsyncHandler(),
14163a5f5eafb34eaa4963c801882148e8f61514a61bJeff Sharkey                        new OnCloseListener() {
1417495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    @Override
1418495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    public void onClose(IOException e) {
1419495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        final ContentValues values = new ContentValues();
1420495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, file.length());
1421495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION,
1422495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                                System.currentTimeMillis());
1423495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        update(uri, values, null, null);
1424495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey
1425495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        if (shouldScan) {
1426495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            final Intent intent = new Intent(
1427495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                                    Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
1428495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            intent.setData(Uri.fromFile(file));
1429495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                            getContext().sendBroadcast(intent);
1430495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                        }
1431495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                    }
1432495edec1d9f7659923c71b009db66c1bd4782034Jeff Sharkey                });
1433c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            } catch (IOException e) {
1434c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey                throw new FileNotFoundException("Failed to open for writing: " + e);
1435c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey            }
1436c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey        }
143757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
143857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
14391d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    @Override
14401d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
14411d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ", 120);
14421d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
14431d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.println("Downloads updated in last hour:");
14441d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.increaseIndent();
14451d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
14461d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
14471d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final long modifiedAfter = mSystemFacade.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS;
14481d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        final Cursor cursor = db.query(DB_TABLE, null,
14491d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                Downloads.Impl.COLUMN_LAST_MODIFICATION + ">" + modifiedAfter, null, null, null,
14501d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                Downloads.Impl._ID + " ASC");
14511d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        try {
14521d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            final String[] cols = cursor.getColumnNames();
14531d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            final int idCol = cursor.getColumnIndex(BaseColumns._ID);
14541d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            while (cursor.moveToNext()) {
14551d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.println("Download #" + cursor.getInt(idCol) + ":");
14561d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.increaseIndent();
14571d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                for (int i = 0; i < cols.length; i++) {
14581d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    // Omit sensitive data when dumping
14591d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    if (Downloads.Impl.COLUMN_COOKIE_DATA.equals(cols[i])) {
14601d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                        continue;
14611d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    }
14621d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                    pw.printPair(cols[i], cursor.getString(i));
14631d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                }
14641d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.println();
14651d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey                pw.decreaseIndent();
14661d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            }
14671d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        } finally {
14681d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey            cursor.close();
14691d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        }
14701d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
14711d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey        pw.decreaseIndent();
14721d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey    }
14731d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey
14743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseOpenFileInfo(Uri uri, String mode) {
14759b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker        Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode
14763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                + ", uid: " + Binder.getCallingUid());
14773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(Downloads.Impl.CONTENT_URI,
14783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                new String[] { "_id" }, null, null, "_id");
14793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
14809b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "null cursor in openFile");
14813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
1482a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            try {
1483a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                if (!cursor.moveToFirst()) {
1484a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "empty cursor in openFile");
1485a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } else {
1486a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    do {
1487a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available");
1488a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    } while(cursor.moveToNext());
1489a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                }
1490a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            } finally {
1491a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                cursor.close();
14923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
14933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
14943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        cursor = query(uri, new String[] { "_data" }, null, null, null);
14953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
14969b731a5521f569c91aeb419d43fa098a34cf78cbDoug Zongker            Log.v(Constants.TAG, "null cursor in openFile");
14973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
1498a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            try {
1499a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                if (!cursor.moveToFirst()) {
1500a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "empty cursor in openFile");
1501a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                } else {
1502a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    String filename = cursor.getString(0);
1503a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    Log.v(Constants.TAG, "filename in openFile: " + filename);
1504a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    if (new java.io.File(filename).isFile()) {
1505a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                        Log.v(Constants.TAG, "file exists in openFile");
1506a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                    }
15073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
1508a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson            } finally {
1509a2aad3aa41402afe88a2bf7b820f3459f795d079Mattias Nilsson                cursor.close();
15103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
15113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
15123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
15133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
15141fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyInteger(String key, ContentValues from, ContentValues to) {
15151fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Integer i = from.getAsInteger(key);
15161fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (i != null) {
15171fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, i);
15181fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
15191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
15201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
15211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyBoolean(String key, ContentValues from, ContentValues to) {
15221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Boolean b = from.getAsBoolean(key);
15231fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (b != null) {
15241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, b);
15251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
15261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
15271fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
15281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyString(String key, ContentValues from, ContentValues to) {
15291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        String s = from.getAsString(key);
15301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (s != null) {
15311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, s);
15321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
15331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
15341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1535a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    private static final void copyStringWithDefault(String key, ContentValues from,
1536a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            ContentValues to, String defaultValue) {
1537a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyString(key, from, to);
1538a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        if (!to.containsKey(key)) {
1539a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            to.put(key, defaultValue);
1540a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        }
1541a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    }
15427c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey
15432ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla    private void grantAllDownloadsPermission(String toPackage, long id) {
15447c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
15452ab9a2d15c63cd567805adb8fa4b9c524afc5cebSuprabh Shukla        getContext().grantUriPermission(toPackage, uri,
15467c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
15477c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey    }
15487c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey
15497c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey    private void revokeAllDownloadsPermission(long id) {
15507c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
15517c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey        getContext().revokeUriPermission(uri, ~0);
15527c1af8c62c8bdf6e8de5a00c1927daf9fd9c03d1Jeff Sharkey    }
155357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project}
1554