DownloadProvider.java revision 1225c34df262ece7a9f95ee5fe61c1985bf16df1
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 193ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.app.DownloadManager; 2051cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkeyimport android.app.DownloadManager.Request; 2157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentProvider; 223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howardimport android.content.ContentUris; 2357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentValues; 2457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Context; 2557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Intent; 2657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.UriMatcher; 2791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.ApplicationInfo; 2857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.pm.PackageManager; 2991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.PackageManager.NameNotFoundException; 3057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.Cursor; 313ca67748bc92eac89f731796c5597ff1fbe9217bVasu Noriimport android.database.DatabaseUtils; 32c6f5aad265cfc36a64cd2bdb5adf3cc9736bbd80Jean-Baptiste Queruimport android.database.SQLException; 3357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase; 3457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper; 3557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.net.Uri; 3657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Binder; 37b06b739b078ce4b00600487cfec31659647bf31fSteve Howardimport android.os.Environment; 38c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport android.os.Handler; 3957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.ParcelFileDescriptor; 40c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport android.os.ParcelFileDescriptor.OnCloseListener; 4157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Process; 42445b908259dac16f32664521c77d94959db7ded3Geremy Condraimport android.os.SELinux; 431d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.provider.BaseColumns; 4457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.provider.Downloads; 459b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport android.provider.OpenableColumns; 46c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkeyimport android.text.TextUtils; 471d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport android.text.format.DateUtils; 4857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.util.Log; 4957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 501d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport com.android.internal.util.IndentingPrintWriter; 519b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport com.google.android.collect.Maps; 525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport com.google.common.annotations.VisibleForTesting; 535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 54c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkeyimport libcore.io.IoUtils; 55c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey 561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.io.File; 571d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.FileDescriptor; 5857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport java.io.FileNotFoundException; 59d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkeyimport java.io.IOException; 601d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkeyimport java.io.PrintWriter; 61e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.ArrayList; 6201d0182d86db003b2da5b831cb26820093888d9aVasu Noriimport java.util.Arrays; 639b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkeyimport java.util.HashMap; 641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.util.HashSet; 65e61798da80558450f580ed948d0d469bd6423d8eSteve Howardimport java.util.Iterator; 66e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.List; 675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport java.util.Map; 681fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 6957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/** 7057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Allows application to interact with the download manager. 7157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 7257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpublic final class DownloadProvider extends ContentProvider { 7357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** Database filename */ 7457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private static final String DB_NAME = "downloads.db"; 751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project /** Current database version */ 76c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey private static final int DB_VERSION = 109; 7757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** Name of table in the database */ 7857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private static final String DB_TABLE = "downloads"; 7957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 8057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** MIME type for the entire download list */ 8157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download"; 8257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** MIME type for an individual download */ 8357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download"; 8457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 8557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** URI matcher used to recognize URIs sent by applications */ 8657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); 873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard /** URI matcher constant for the URI of all downloads belonging to the calling UID */ 883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final int MY_DOWNLOADS = 1; 893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard /** URI matcher constant for the URI of an individual download belonging to the calling UID */ 903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final int MY_DOWNLOADS_ID = 2; 913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard /** URI matcher constant for the URI of all downloads in the system */ 923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final int ALL_DOWNLOADS = 3; 9357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** URI matcher constant for the URI of an individual download */ 943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final int ALL_DOWNLOADS_ID = 4; 955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** URI matcher constant for the URI of a download's request headers */ 963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final int REQUEST_HEADERS_URI = 5; 973ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori /** URI matcher constant for the public URI returned by 983ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori * {@link DownloadManager#getUriForDownloadedFile(long)} if the given downloaded file 993ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori * is publicly accessible. 1003ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori */ 1013ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori private static final int PUBLIC_DOWNLOAD_ID = 6; 10257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project static { 1033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS); 1043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID); 1053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS); 1063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID); 1073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", 1083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard "my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT, 1093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard REQUEST_HEADERS_URI); 1103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sURIMatcher.addURI("downloads", 1113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard "all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT, 1123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard REQUEST_HEADERS_URI); 1134bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard // temporary, for backwards compatibility 1144bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS); 1154bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID); 1164bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard sURIMatcher.addURI("downloads", 1174bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT, 1184bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard REQUEST_HEADERS_URI); 1193ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori sURIMatcher.addURI("downloads", 1203ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT + "/#", 1213ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori PUBLIC_DOWNLOAD_ID); 12257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 12357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 1243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard /** Different base URIs that could be used to access an individual download */ 1253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private static final Uri[] BASE_URIS = new Uri[] { 1263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Downloads.Impl.CONTENT_URI, 1273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, 1283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard }; 1293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 1301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project private static final String[] sAppReadableColumnsArray = new String[] { 1317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl._ID, 1327dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_APP_DATA, 1337dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl._DATA, 1347dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_MIME_TYPE, 1357dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_VISIBILITY, 1367dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_DESTINATION, 1377dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_CONTROL, 1387dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_STATUS, 1397dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_LAST_MODIFICATION, 1407dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, 1417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_NOTIFICATION_CLASS, 1427dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_TOTAL_BYTES, 1437dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_CURRENT_BYTES, 1447dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.COLUMN_TITLE, 1450a77c62a82503b38c484e0079648f0231dd85d53Steve Howard Downloads.Impl.COLUMN_DESCRIPTION, 1460d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard Downloads.Impl.COLUMN_URI, 14771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, 148b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard Downloads.Impl.COLUMN_FILE_NAME_HINT, 149e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, 150e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori Downloads.Impl.COLUMN_DELETED, 1519b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey OpenableColumns.DISPLAY_NAME, 1529b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey OpenableColumns.SIZE, 1531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project }; 1541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 1559b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey private static final HashSet<String> sAppReadableColumnsSet; 1569b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey private static final HashMap<String, String> sColumnsMap; 1579b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey 1581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project static { 1591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project sAppReadableColumnsSet = new HashSet<String>(); 1601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project for (int i = 0; i < sAppReadableColumnsArray.length; ++i) { 1611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]); 1621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 1639b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey 1649b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey sColumnsMap = Maps.newHashMap(); 1659b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey sColumnsMap.put(OpenableColumns.DISPLAY_NAME, 1669b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey Downloads.Impl.COLUMN_TITLE + " AS " + OpenableColumns.DISPLAY_NAME); 1679b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey sColumnsMap.put(OpenableColumns.SIZE, 1689b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + OpenableColumns.SIZE); 1691fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 17001d0182d86db003b2da5b831cb26820093888d9aVasu Nori private static final List<String> downloadManagerColumnsList = 17101d0182d86db003b2da5b831cb26820093888d9aVasu Nori Arrays.asList(DownloadManager.UNDERLYING_COLUMNS); 1721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 173c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey private Handler mHandler; 174c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey 17557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** The database that lies underneath this content provider */ 17657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private SQLiteOpenHelper mOpenHelper = null; 17757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 17891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu /** List of uids that can access the downloads */ 17991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu private int mSystemUid = -1; 18091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu private int mDefContainerUid = -1; 1819aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori private File mDownloadsDataDir; 18291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu 1836d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard @VisibleForTesting 1846d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard SystemFacade mSystemFacade; 1856d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard 18657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 187e610c0502c00689411624c00c3f81497df93b202Steve Howard * This class encapsulates a SQL where clause and its parameters. It makes it possible for 188e610c0502c00689411624c00c3f81497df93b202Steve Howard * shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)}) 189e610c0502c00689411624c00c3f81497df93b202Steve Howard * to return both pieces of information, and provides some utility logic to ease piece-by-piece 190e610c0502c00689411624c00c3f81497df93b202Steve Howard * construction of selections. 191e610c0502c00689411624c00c3f81497df93b202Steve Howard */ 192e610c0502c00689411624c00c3f81497df93b202Steve Howard private static class SqlSelection { 193e610c0502c00689411624c00c3f81497df93b202Steve Howard public StringBuilder mWhereClause = new StringBuilder(); 194e610c0502c00689411624c00c3f81497df93b202Steve Howard public List<String> mParameters = new ArrayList<String>(); 195e610c0502c00689411624c00c3f81497df93b202Steve Howard 196e610c0502c00689411624c00c3f81497df93b202Steve Howard public <T> void appendClause(String newClause, final T... parameters) { 197e610c0502c00689411624c00c3f81497df93b202Steve Howard if (newClause == null || newClause.isEmpty()) { 198e610c0502c00689411624c00c3f81497df93b202Steve Howard return; 199e610c0502c00689411624c00c3f81497df93b202Steve Howard } 200e610c0502c00689411624c00c3f81497df93b202Steve Howard if (mWhereClause.length() != 0) { 201e610c0502c00689411624c00c3f81497df93b202Steve Howard mWhereClause.append(" AND "); 202e610c0502c00689411624c00c3f81497df93b202Steve Howard } 203e610c0502c00689411624c00c3f81497df93b202Steve Howard mWhereClause.append("("); 204e610c0502c00689411624c00c3f81497df93b202Steve Howard mWhereClause.append(newClause); 205e610c0502c00689411624c00c3f81497df93b202Steve Howard mWhereClause.append(")"); 206e610c0502c00689411624c00c3f81497df93b202Steve Howard if (parameters != null) { 207e610c0502c00689411624c00c3f81497df93b202Steve Howard for (Object parameter : parameters) { 208e610c0502c00689411624c00c3f81497df93b202Steve Howard mParameters.add(parameter.toString()); 209e610c0502c00689411624c00c3f81497df93b202Steve Howard } 210e610c0502c00689411624c00c3f81497df93b202Steve Howard } 211e610c0502c00689411624c00c3f81497df93b202Steve Howard } 212e610c0502c00689411624c00c3f81497df93b202Steve Howard 213e610c0502c00689411624c00c3f81497df93b202Steve Howard public String getSelection() { 214e610c0502c00689411624c00c3f81497df93b202Steve Howard return mWhereClause.toString(); 215e610c0502c00689411624c00c3f81497df93b202Steve Howard } 216e610c0502c00689411624c00c3f81497df93b202Steve Howard 217e610c0502c00689411624c00c3f81497df93b202Steve Howard public String[] getParameters() { 218e610c0502c00689411624c00c3f81497df93b202Steve Howard String[] array = new String[mParameters.size()]; 219e610c0502c00689411624c00c3f81497df93b202Steve Howard return mParameters.toArray(array); 220e610c0502c00689411624c00c3f81497df93b202Steve Howard } 221e610c0502c00689411624c00c3f81497df93b202Steve Howard } 222e610c0502c00689411624c00c3f81497df93b202Steve Howard 223e610c0502c00689411624c00c3f81497df93b202Steve Howard /** 22457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Creates and updated database on demand when opening it. 22557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Helper class to create database the first time the provider is 22657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * initialized and upgrade it when a new version of the provider needs 22757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * an updated version of the database. 22857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 22957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project private final class DatabaseHelper extends SQLiteOpenHelper { 23057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public DatabaseHelper(final Context context) { 23157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project super(context, DB_NAME, null, DB_VERSION); 23257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 23357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 23457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 23557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Creates database the first time we try to open it. 23657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 23757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 23857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public void onCreate(final SQLiteDatabase db) { 23957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGVV) { 24057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project Log.v(Constants.TAG, "populating new database"); 24157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 2425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard onUpgrade(db, 0, DB_VERSION); 24357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 24457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 24557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 24657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Updates the database format when a content provider is used 24757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * with a database that was created with a different format. 2485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * 2495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Note: to support downgrades, creating a table should always drop it first if it already 2505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * exists. 25157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 25257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 2531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) { 2545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard if (oldV == 31) { 2555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard // 31 and 100 are identical, just in different codelines. Upgrading from 31 is the 2565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard // same as upgrading from 100. 2575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard oldV = 100; 2585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } else if (oldV < 100) { 2595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard // no logic to upgrade from these older version, just recreate the DB 2605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV 2615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard + " to version " + newV + ", which will destroy all old data"); 2625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard oldV = 99; 2635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } else if (oldV > newV) { 2645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard // user must have downgraded software; we have no way to know how to downgrade the 2655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard // DB, so just recreate it 2665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Log.i(Constants.TAG, "Downgrading downloads database from version " + oldV 2675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard + " (current version is " + newV + "), destroying all old data"); 2685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard oldV = 99; 2695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 2705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 2715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard for (int version = oldV + 1; version <= newV; version++) { 2725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard upgradeTo(db, version); 2735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 2745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 2755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 2765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 2775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Upgrade database from (version - 1) to version. 2785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 2795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private void upgradeTo(SQLiteDatabase db, int version) { 2805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard switch (version) { 2815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard case 100: 2825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard createDownloadsTable(db); 2835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard break; 2845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 2855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard case 101: 2865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard createHeadersTable(db); 2875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard break; 2885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 2890a77c62a82503b38c484e0079648f0231dd85d53Steve Howard case 102: 2900a77c62a82503b38c484e0079648f0231dd85d53Steve Howard addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_PUBLIC_API, 2910a77c62a82503b38c484e0079648f0231dd85d53Steve Howard "INTEGER NOT NULL DEFAULT 0"); 2920a77c62a82503b38c484e0079648f0231dd85d53Steve Howard addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_ROAMING, 2930a77c62a82503b38c484e0079648f0231dd85d53Steve Howard "INTEGER NOT NULL DEFAULT 0"); 2940a77c62a82503b38c484e0079648f0231dd85d53Steve Howard addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, 2950a77c62a82503b38c484e0079648f0231dd85d53Steve Howard "INTEGER NOT NULL DEFAULT 0"); 2960a77c62a82503b38c484e0079648f0231dd85d53Steve Howard break; 2970a77c62a82503b38c484e0079648f0231dd85d53Steve Howard 29871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard case 103: 29971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, 30071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard "INTEGER NOT NULL DEFAULT 1"); 30171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard makeCacheDownloadsInvisible(db); 30271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard break; 30371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard 304d319729622da1893e895f2e35f41d01ecdca3705Steve Howard case 104: 305d319729622da1893e895f2e35f41d01ecdca3705Steve Howard addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT, 306d319729622da1893e895f2e35f41d01ecdca3705Steve Howard "INTEGER NOT NULL DEFAULT 0"); 307d319729622da1893e895f2e35f41d01ecdca3705Steve Howard break; 308d319729622da1893e895f2e35f41d01ecdca3705Steve Howard 30973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard case 105: 31073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard fillNullValues(db); 31173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard break; 31273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard 313e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori case 106: 314e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, "TEXT"); 315e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_DELETED, 316e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori "BOOLEAN NOT NULL DEFAULT 0"); 317e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori break; 318e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori 3199d27069a5453574824860ad3db179599d044e7bdVasu Nori case 107: 3209d27069a5453574824860ad3db179599d044e7bdVasu Nori addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ERROR_MSG, "TEXT"); 3219d27069a5453574824860ad3db179599d044e7bdVasu Nori break; 3229d27069a5453574824860ad3db179599d044e7bdVasu Nori 323a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey case 108: 324a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_METERED, 325a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey "INTEGER NOT NULL DEFAULT 1"); 326a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey break; 327a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey 328c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey case 109: 329c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_WRITE, 330c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey "BOOLEAN NOT NULL DEFAULT 0"); 331c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey break; 332c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey 3335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard default: 3345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard throw new IllegalStateException("Don't know how to upgrade to " + version); 3355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 3365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 3375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 3385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 33973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard * insert() now ensures these four columns are never null for new downloads, so this method 34073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard * makes that true for existing columns, so that code can rely on this assumption. 34173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard */ 34273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard private void fillNullValues(SQLiteDatabase db) { 34373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard ContentValues values = new ContentValues(); 34473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0); 34573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard fillNullValuesForColumn(db, values); 34673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1); 34773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard fillNullValuesForColumn(db, values); 34873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard values.put(Downloads.Impl.COLUMN_TITLE, ""); 34973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard fillNullValuesForColumn(db, values); 35073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard values.put(Downloads.Impl.COLUMN_DESCRIPTION, ""); 35173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard fillNullValuesForColumn(db, values); 35273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard } 35373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard 35473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard private void fillNullValuesForColumn(SQLiteDatabase db, ContentValues values) { 35573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard String column = values.valueSet().iterator().next().getKey(); 35673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard db.update(DB_TABLE, values, column + " is null", null); 35773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard values.clear(); 35873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard } 35973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard 36073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard /** 36171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard * Set all existing downloads to the cache partition to be invisible in the downloads UI. 36271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard */ 36371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard private void makeCacheDownloadsInvisible(SQLiteDatabase db) { 36471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard ContentValues values = new ContentValues(); 36571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false); 36671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard String cacheSelection = Downloads.Impl.COLUMN_DESTINATION 36771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard + " != " + Downloads.Impl.DESTINATION_EXTERNAL; 36871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard db.update(DB_TABLE, values, cacheSelection, null); 36971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard } 37071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard 37171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard /** 3720a77c62a82503b38c484e0079648f0231dd85d53Steve Howard * Add a column to a table using ALTER TABLE. 3730a77c62a82503b38c484e0079648f0231dd85d53Steve Howard * @param dbTable name of the table 3740a77c62a82503b38c484e0079648f0231dd85d53Steve Howard * @param columnName name of the column to add 3750a77c62a82503b38c484e0079648f0231dd85d53Steve Howard * @param columnDefinition SQL for the column definition 3760a77c62a82503b38c484e0079648f0231dd85d53Steve Howard */ 3770a77c62a82503b38c484e0079648f0231dd85d53Steve Howard private void addColumn(SQLiteDatabase db, String dbTable, String columnName, 3780a77c62a82503b38c484e0079648f0231dd85d53Steve Howard String columnDefinition) { 3790a77c62a82503b38c484e0079648f0231dd85d53Steve Howard db.execSQL("ALTER TABLE " + dbTable + " ADD COLUMN " + columnName + " " 3800a77c62a82503b38c484e0079648f0231dd85d53Steve Howard + columnDefinition); 3810a77c62a82503b38c484e0079648f0231dd85d53Steve Howard } 3820a77c62a82503b38c484e0079648f0231dd85d53Steve Howard 3830a77c62a82503b38c484e0079648f0231dd85d53Steve Howard /** 3845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Creates the table that'll hold the download information. 3855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 3865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private void createDownloadsTable(SQLiteDatabase db) { 3875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard try { 3885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); 3895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.execSQL("CREATE TABLE " + DB_TABLE + "(" + 3905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 3915224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_URI + " TEXT, " + 3925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, " + 3935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_APP_DATA + " TEXT, " + 3945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_NO_INTEGRITY + " BOOLEAN, " + 3955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_FILE_NAME_HINT + " TEXT, " + 396f20af91262fecce05928167123c8d335b4cfd33dVasu Nori Constants.OTA_UPDATE + " BOOLEAN, " + 3975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl._DATA + " TEXT, " + 3985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_MIME_TYPE + " TEXT, " + 3995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_DESTINATION + " INTEGER, " + 4005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Constants.NO_SYSTEM_FILES + " BOOLEAN, " + 4015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_VISIBILITY + " INTEGER, " + 4025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_CONTROL + " INTEGER, " + 4035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_STATUS + " INTEGER, " + 40412f5dc46aaa8e28cabfbe25d55f0af68f24ab306Jeff Sharkey Downloads.Impl.COLUMN_FAILED_CONNECTIONS + " INTEGER, " + 4055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_LAST_MODIFICATION + " BIGINT, " + 4065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + " TEXT, " + 4075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_NOTIFICATION_CLASS + " TEXT, " + 4085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + " TEXT, " + 4095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_COOKIE_DATA + " TEXT, " + 4105224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_USER_AGENT + " TEXT, " + 4115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_REFERER + " TEXT, " + 4125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_TOTAL_BYTES + " INTEGER, " + 4135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_CURRENT_BYTES + " INTEGER, " + 4145224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Constants.ETAG + " TEXT, " + 4155224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Constants.UID + " INTEGER, " + 4165224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_OTHER_UID + " INTEGER, " + 4175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_TITLE + " TEXT, " + 4185224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.COLUMN_DESCRIPTION + " TEXT, " + 419a53c21edb5dc57d97dcddd03fbfa2022abf43787Vasu Nori Constants.MEDIA_SCANNED + " BOOLEAN);"); 4205224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } catch (SQLException ex) { 4215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Log.e(Constants.TAG, "couldn't create table in downloads database"); 4225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard throw ex; 4235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 4245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 4255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 4265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private void createHeadersTable(SQLiteDatabase db) { 4275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.execSQL("DROP TABLE IF EXISTS " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE); 4285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.execSQL("CREATE TABLE " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + "(" + 4295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard "id INTEGER PRIMARY KEY AUTOINCREMENT," + 4305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL," + 4315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL," + 4325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL" + 4335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard ");"); 43457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 43557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 43657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 43757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 43857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Initializes the content provider when it is created. 43957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 44057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 44157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public boolean onCreate() { 4426d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard if (mSystemFacade == null) { 443af28400b74de05862b470412a5c92f68e99f59f8Steve Howard mSystemFacade = new RealSystemFacade(getContext()); 4446d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard } 4456d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard 446c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey mHandler = new Handler(); 447c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey 44857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project mOpenHelper = new DatabaseHelper(getContext()); 44991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu // Initialize the system uid 45091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu mSystemUid = Process.SYSTEM_UID; 45191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu // Initialize the default container uid. Package name hardcoded 45291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu // for now. 45391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu ApplicationInfo appInfo = null; 45491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu try { 45591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu appInfo = getContext().getPackageManager(). 45691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu getApplicationInfo("com.android.defcontainer", 0); 45791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu } catch (NameNotFoundException e) { 458445b908259dac16f32664521c77d94959db7ded3Geremy Condra Log.wtf(Constants.TAG, "Could not get ApplicationInfo for com.android.defconatiner", e); 45991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu } 46091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu if (appInfo != null) { 46191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu mDefContainerUid = appInfo.uid; 46291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu } 4635218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori // start the DownloadService class. don't wait for the 1st download to be issued. 4645218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori // saves us by getting some initialization code in DownloadService out of the way. 4655218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori Context context = getContext(); 4665218d33d57990c3e3549c58bd3f0ac244dfc3d59Vasu Nori context.startService(new Intent(context, DownloadService.class)); 467701d66efeff513a7509eeaafab6e47f4f6edb857Jeff Sharkey mDownloadsDataDir = StorageManager.getDownloadDataDirectory(getContext()); 468445b908259dac16f32664521c77d94959db7ded3Geremy Condra try { 469445b908259dac16f32664521c77d94959db7ded3Geremy Condra SELinux.restorecon(mDownloadsDataDir.getCanonicalPath()); 470445b908259dac16f32664521c77d94959db7ded3Geremy Condra } catch (IOException e) { 471445b908259dac16f32664521c77d94959db7ded3Geremy Condra Log.wtf(Constants.TAG, "Could not get canonical path for download directory", e); 472445b908259dac16f32664521c77d94959db7ded3Geremy Condra } 47357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project return true; 47457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 47557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 47657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 47757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Returns the content-provider-style MIME types of the various 47857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * types accessible through this content provider. 47957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 48057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 48157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public String getType(final Uri uri) { 48257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int match = sURIMatcher.match(uri); 48357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project switch (match) { 4849b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey case MY_DOWNLOADS: 4859b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey case ALL_DOWNLOADS: { 48657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project return DOWNLOAD_LIST_TYPE; 48757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 4889b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey case MY_DOWNLOADS_ID: 489c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey case ALL_DOWNLOADS_ID: 4903ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori case PUBLIC_DOWNLOAD_ID: { 4913ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori // return the mimetype of this id from the database 4923ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori final String id = getDownloadIdFromUri(uri); 4933ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 494c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey final String mimeType = DatabaseUtils.stringForQuery(db, 4953ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori "SELECT " + Downloads.Impl.COLUMN_MIME_TYPE + " FROM " + DB_TABLE + 4963ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori " WHERE " + Downloads.Impl._ID + " = ?", 4973ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori new String[]{id}); 498c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey if (TextUtils.isEmpty(mimeType)) { 499c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey return DOWNLOAD_TYPE; 500c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey } else { 501c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey return mimeType; 502c3f3d992e415185a8e2d89ab8f8dfbcb538ec21eJeff Sharkey } 5033ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori } 50457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project default: { 50557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGV) { 50657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri); 50757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 50857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project throw new IllegalArgumentException("Unknown URI: " + uri); 50957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 51057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 51157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 51257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 51357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 51457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Inserts a row in the database 51557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 51657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 51757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public Uri insert(final Uri uri, final ContentValues values) { 518b06b739b078ce4b00600487cfec31659647bf31fSteve Howard checkInsertPermissions(values); 51957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 52057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 5213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard // note we disallow inserting into ALL_DOWNLOADS 5223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard int match = sURIMatcher.match(uri); 5233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (match != MY_DOWNLOADS) { 5243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri); 52557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project throw new IllegalArgumentException("Unknown/Invalid URI " + uri); 52657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 52757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 528b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // copy some of the input values as it 5291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project ContentValues filteredValues = new ContentValues(); 5307dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_URI, values, filteredValues); 5317dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues); 5327dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues); 5337dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues); 5347dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues); 53571aab521efba9b28779541440c797220ec98ac97Steve Howard copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues); 536b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 53771aab521efba9b28779541440c797220ec98ac97Steve Howard boolean isPublicApi = 53871aab521efba9b28779541440c797220ec98ac97Steve Howard values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE; 53971aab521efba9b28779541440c797220ec98ac97Steve Howard 540b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // validate the destination column 5417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Integer dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION); 542ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru if (dest != null) { 5437dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED) 5441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project != PackageManager.PERMISSION_GRANTED 545b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori && (dest == Downloads.Impl.DESTINATION_CACHE_PARTITION 546b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori || dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING 547b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori || dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION)) { 5489aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori throw new SecurityException("setting destination to : " + dest + 5499aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori " not allowed, unless PERMISSION_ACCESS_ADVANCED is granted"); 5501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 55171aab521efba9b28779541440c797220ec98ac97Steve Howard // for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically 55271aab521efba9b28779541440c797220ec98ac97Steve Howard // switch to non-purgeable download 55371aab521efba9b28779541440c797220ec98ac97Steve Howard boolean hasNonPurgeablePermission = 55471aab521efba9b28779541440c797220ec98ac97Steve Howard getContext().checkCallingPermission( 55571aab521efba9b28779541440c797220ec98ac97Steve Howard Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE) 55671aab521efba9b28779541440c797220ec98ac97Steve Howard == PackageManager.PERMISSION_GRANTED; 55771aab521efba9b28779541440c797220ec98ac97Steve Howard if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE 55871aab521efba9b28779541440c797220ec98ac97Steve Howard && hasNonPurgeablePermission) { 55971aab521efba9b28779541440c797220ec98ac97Steve Howard dest = Downloads.Impl.DESTINATION_CACHE_PARTITION; 56071aab521efba9b28779541440c797220ec98ac97Steve Howard } 5616d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard if (dest == Downloads.Impl.DESTINATION_FILE_URI) { 5626d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard getContext().enforcePermission( 5636d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard android.Manifest.permission.WRITE_EXTERNAL_STORAGE, 5646d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard Binder.getCallingPid(), Binder.getCallingUid(), 5656d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard "need WRITE_EXTERNAL_STORAGE permission to use DESTINATION_FILE_URI"); 566b06b739b078ce4b00600487cfec31659647bf31fSteve Howard checkFileUriDestination(values); 5679aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori } else if (dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) { 5689aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori getContext().enforcePermission( 5699aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori android.Manifest.permission.ACCESS_CACHE_FILESYSTEM, 5709aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori Binder.getCallingPid(), Binder.getCallingUid(), 5719aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori "need ACCESS_CACHE_FILESYSTEM permission to use system cache"); 5726d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard } 5737dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest); 574ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru } 575b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 576b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // validate the visibility column 5777dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Integer vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY); 578ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru if (vis == null) { 5797dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru if (dest == Downloads.Impl.DESTINATION_EXTERNAL) { 5807dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, 5817dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 582ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru } else { 5837dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, 5847dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Downloads.Impl.VISIBILITY_HIDDEN); 5851fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 586ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru } else { 5877dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis); 58857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 589b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // copy the control column as is 5907dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues); 591b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 592b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori /* 593b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori * requests coming from 5949b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori * DownloadManager.addCompletedDownload(String, String, String, 5959b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori * boolean, String, String, long) need special treatment 596b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori */ 597b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) == 598b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) { 599b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // these requests always are marked as 'completed' 600b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS); 601b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, 602b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori values.getAsLong(Downloads.Impl.COLUMN_TOTAL_BYTES)); 603b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0); 604b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori copyInteger(Downloads.Impl.COLUMN_MEDIA_SCANNED, values, filteredValues); 605b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori copyString(Downloads.Impl._DATA, values, filteredValues); 606c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey copyBoolean(Downloads.Impl.COLUMN_ALLOW_WRITE, values, filteredValues); 607b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori } else { 608b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING); 609b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1); 610b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0); 611b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori } 612b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 613b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // set lastupdate to current time 6142c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori long lastMod = mSystemFacade.currentTimeMillis(); 6152c02577af19bf11714220d14cfc96d2c017ac1abVasu Nori filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, lastMod); 6160a77c62a82503b38c484e0079648f0231dd85d53Steve Howard 617b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // use packagename of the caller to set the notification columns 6187dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru String pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); 6197dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS); 6200a77c62a82503b38c484e0079648f0231dd85d53Steve Howard if (pckg != null && (clazz != null || isPublicApi)) { 6211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project int uid = Binder.getCallingUid(); 6221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project try { 6230a77c62a82503b38c484e0079648f0231dd85d53Steve Howard if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) { 6247dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg); 6250a77c62a82503b38c484e0079648f0231dd85d53Steve Howard if (clazz != null) { 6260a77c62a82503b38c484e0079648f0231dd85d53Steve Howard filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz); 6270a77c62a82503b38c484e0079648f0231dd85d53Steve Howard } 6281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 6291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } catch (PackageManager.NameNotFoundException ex) { 6301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project /* ignored for now */ 63157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 63257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 633b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 634b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // copy some more columns as is 6357dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues); 6367dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues); 6377dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues); 6387dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues); 639b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 640b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // UID, PID columns 6417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED) 6421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project == PackageManager.PERMISSION_GRANTED) { 6437dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues); 64457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 6451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project filteredValues.put(Constants.UID, Binder.getCallingUid()); 6461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (Binder.getCallingUid() == 0) { 6471fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project copyInteger(Constants.UID, values, filteredValues); 64857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 649b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 650b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // copy some more columns as is 651a89321ea04ced76d06f60f5909be203cb654a830Steve Howard copyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, ""); 652a89321ea04ced76d06f60f5909be203cb654a830Steve Howard copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, ""); 65357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 654b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // is_visible_in_downloads_ui column 65571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) { 65671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues); 65771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard } else { 65871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard // by default, make external downloads visible in the UI 65971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL); 66071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal); 66171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard } 66271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard 663b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // public api requests and networktypes/roaming columns 6640a77c62a82503b38c484e0079648f0231dd85d53Steve Howard if (isPublicApi) { 6650a77c62a82503b38c484e0079648f0231dd85d53Steve Howard copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues); 6660a77c62a82503b38c484e0079648f0231dd85d53Steve Howard copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues); 667a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues); 6680a77c62a82503b38c484e0079648f0231dd85d53Steve Howard } 6690a77c62a82503b38c484e0079648f0231dd85d53Steve Howard 6701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (Constants.LOGVV) { 6711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Log.v(Constants.TAG, "initiating download with UID " 6721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project + filteredValues.getAsInteger(Constants.UID)); 6737dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) { 6741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Log.v(Constants.TAG, "other UID " + 6757dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID)); 6761fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 67757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 67857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 6791fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project long rowID = db.insert(DB_TABLE, null, filteredValues); 6803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (rowID == -1) { 6813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.d(Constants.TAG, "couldn't insert into downloads database"); 6823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard return null; 68357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 68457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 6853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard insertRequestHeaders(db, rowID, values); 6863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard notifyContentChanged(uri, match); 6871225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey 6881225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey // Always start service to handle notifications and/or scanning 6891225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey final Context context = getContext(); 6901225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey context.startService(new Intent(context, DownloadService.class)); 6911225c34df262ece7a9f95ee5fe61c1985bf16df1Jeff Sharkey 6923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID); 69357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 69457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 69557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 696b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * Check that the file URI provided for DESTINATION_FILE_URI is valid. 697b06b739b078ce4b00600487cfec31659647bf31fSteve Howard */ 698b06b739b078ce4b00600487cfec31659647bf31fSteve Howard private void checkFileUriDestination(ContentValues values) { 699b06b739b078ce4b00600487cfec31659647bf31fSteve Howard String fileUri = values.getAsString(Downloads.Impl.COLUMN_FILE_NAME_HINT); 700b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (fileUri == null) { 701b06b739b078ce4b00600487cfec31659647bf31fSteve Howard throw new IllegalArgumentException( 702b06b739b078ce4b00600487cfec31659647bf31fSteve Howard "DESTINATION_FILE_URI must include a file URI under COLUMN_FILE_NAME_HINT"); 703b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 704b06b739b078ce4b00600487cfec31659647bf31fSteve Howard Uri uri = Uri.parse(fileUri); 7055d81e2447ed77860afecd71583e137178c2c6807Steve Howard String scheme = uri.getScheme(); 7065d81e2447ed77860afecd71583e137178c2c6807Steve Howard if (scheme == null || !scheme.equals("file")) { 707b06b739b078ce4b00600487cfec31659647bf31fSteve Howard throw new IllegalArgumentException("Not a file URI: " + uri); 708b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 709d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey final String path = uri.getPath(); 710b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard if (path == null) { 711b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard throw new IllegalArgumentException("Invalid file URI: " + uri); 712b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard } 713d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey try { 714d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey final String canonicalPath = new File(path).getCanonicalPath(); 715d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey final String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath(); 716d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey if (!canonicalPath.startsWith(externalPath)) { 717d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey throw new SecurityException("Destination must be on external storage: " + uri); 718d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey } 719d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey } catch (IOException e) { 720d195a5c677e575ba7e96e9366d0823c3c822231dJeff Sharkey throw new SecurityException("Problem resolving path: " + uri); 721b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 722b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 723b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 724b06b739b078ce4b00600487cfec31659647bf31fSteve Howard /** 725b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to 726b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * constraints in the rest of the code. Apps without that may still access this provider through 727b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * the public API, but additional restrictions are imposed. We check those restrictions here. 728b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * 729b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * @param values ContentValues provided to insert() 730b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * @throws SecurityException if the caller has insufficient permissions 731b06b739b078ce4b00600487cfec31659647bf31fSteve Howard */ 732b06b739b078ce4b00600487cfec31659647bf31fSteve Howard private void checkInsertPermissions(ContentValues values) { 733b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS) 734b06b739b078ce4b00600487cfec31659647bf31fSteve Howard == PackageManager.PERMISSION_GRANTED) { 735b06b739b078ce4b00600487cfec31659647bf31fSteve Howard return; 736b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 737b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 738b06b739b078ce4b00600487cfec31659647bf31fSteve Howard getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, 739b06b739b078ce4b00600487cfec31659647bf31fSteve Howard "INTERNET permission is required to use the download manager"); 740b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 741b06b739b078ce4b00600487cfec31659647bf31fSteve Howard // ensure the request fits within the bounds of a public API request 742b06b739b078ce4b00600487cfec31659647bf31fSteve Howard // first copy so we can remove values 743b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values = new ContentValues(values); 744b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 745b06b739b078ce4b00600487cfec31659647bf31fSteve Howard // check columns whose values are restricted 746b06b739b078ce4b00600487cfec31659647bf31fSteve Howard enforceAllowedValues(values, Downloads.Impl.COLUMN_IS_PUBLIC_API, Boolean.TRUE); 747b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori 748b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori // validate the destination column 749b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) == 750b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) { 751b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori /* this row is inserted by 7529b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori * DownloadManager.addCompletedDownload(String, String, String, 7539b2576f9181fc177dbbc9033c435b1d4049fc2e0Vasu Nori * boolean, String, String, long) 754b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori */ 755b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori values.remove(Downloads.Impl.COLUMN_TOTAL_BYTES); 756b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori values.remove(Downloads.Impl._DATA); 757b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori values.remove(Downloads.Impl.COLUMN_STATUS); 758b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori } 759b06b739b078ce4b00600487cfec31659647bf31fSteve Howard enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION, 760b06b739b078ce4b00600487cfec31659647bf31fSteve Howard Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE, 761b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori Downloads.Impl.DESTINATION_FILE_URI, 762b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD); 7639da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard 7649da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION) 7659da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard == PackageManager.PERMISSION_GRANTED) { 7669da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY, 76751cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_HIDDEN, 76851cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE, 76951cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED, 77051cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION); 7719da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard } else { 7729da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY, 77351cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE, 77451cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED, 77551cc2143feeed748c62544c7f1a57415bd90c7afJeff Sharkey Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION); 7769da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard } 777b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 778b06b739b078ce4b00600487cfec31659647bf31fSteve Howard // remove the rest of the columns that are allowed (with any value) 779b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_URI); 780b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_TITLE); 781b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_DESCRIPTION); 782b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_MIME_TYPE); 783b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_FILE_NAME_HINT); // checked later in insert() 784b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert() 785b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES); 786b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING); 787a7ae77fdae69bcc6d6609d4639fed5d96e55eeaaJeff Sharkey values.remove(Downloads.Impl.COLUMN_ALLOW_METERED); 78871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI); 7893002e1870d4ad76c260b4729a8d86212c8db3e78Vasu Nori values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED); 790c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey values.remove(Downloads.Impl.COLUMN_ALLOW_WRITE); 791e61798da80558450f580ed948d0d469bd6423d8eSteve Howard Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator(); 792e61798da80558450f580ed948d0d469bd6423d8eSteve Howard while (iterator.hasNext()) { 793e61798da80558450f580ed948d0d469bd6423d8eSteve Howard String key = iterator.next().getKey(); 794e61798da80558450f580ed948d0d469bd6423d8eSteve Howard if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) { 795e61798da80558450f580ed948d0d469bd6423d8eSteve Howard iterator.remove(); 796e61798da80558450f580ed948d0d469bd6423d8eSteve Howard } 797e61798da80558450f580ed948d0d469bd6423d8eSteve Howard } 798b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 799b06b739b078ce4b00600487cfec31659647bf31fSteve Howard // any extra columns are extraneous and disallowed 800b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (values.size() > 0) { 801b06b739b078ce4b00600487cfec31659647bf31fSteve Howard StringBuilder error = new StringBuilder("Invalid columns in request: "); 802b06b739b078ce4b00600487cfec31659647bf31fSteve Howard boolean first = true; 803b06b739b078ce4b00600487cfec31659647bf31fSteve Howard for (Map.Entry<String, Object> entry : values.valueSet()) { 804b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (!first) { 805b06b739b078ce4b00600487cfec31659647bf31fSteve Howard error.append(", "); 806b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 807b06b739b078ce4b00600487cfec31659647bf31fSteve Howard error.append(entry.getKey()); 808b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 809b06b739b078ce4b00600487cfec31659647bf31fSteve Howard throw new SecurityException(error.toString()); 810b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 811b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 812b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 813b06b739b078ce4b00600487cfec31659647bf31fSteve Howard /** 814b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * Remove column from values, and throw a SecurityException if the value isn't within the 815b06b739b078ce4b00600487cfec31659647bf31fSteve Howard * specified allowedValues. 816b06b739b078ce4b00600487cfec31659647bf31fSteve Howard */ 817b06b739b078ce4b00600487cfec31659647bf31fSteve Howard private void enforceAllowedValues(ContentValues values, String column, 818b06b739b078ce4b00600487cfec31659647bf31fSteve Howard Object... allowedValues) { 819b06b739b078ce4b00600487cfec31659647bf31fSteve Howard Object value = values.get(column); 820b06b739b078ce4b00600487cfec31659647bf31fSteve Howard values.remove(column); 821b06b739b078ce4b00600487cfec31659647bf31fSteve Howard for (Object allowedValue : allowedValues) { 822b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (value == null && allowedValue == null) { 823b06b739b078ce4b00600487cfec31659647bf31fSteve Howard return; 824b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 825b06b739b078ce4b00600487cfec31659647bf31fSteve Howard if (value != null && value.equals(allowedValue)) { 826b06b739b078ce4b00600487cfec31659647bf31fSteve Howard return; 827b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 828b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 829b06b739b078ce4b00600487cfec31659647bf31fSteve Howard throw new SecurityException("Invalid value for " + column + ": " + value); 830b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 831b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 832b06b739b078ce4b00600487cfec31659647bf31fSteve Howard /** 83357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Starts a database query 83457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 83557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 8361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project public Cursor query(final Uri uri, String[] projection, 83757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project final String selection, final String[] selectionArgs, 83857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project final String sort) { 8391fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 8401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Helpers.validateSelection(selection, sAppReadableColumnsSet); 8411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 84257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 84357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 84457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int match = sURIMatcher.match(uri); 8453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (match == -1) { 8463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (Constants.LOGV) { 8473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "querying unknown URI: " + uri); 84857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 8493d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard throw new IllegalArgumentException("Unknown URI: " + uri); 8503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 8513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 8523d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (match == REQUEST_HEADERS_URI) { 8533d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (projection != null || selection != null || sort != null) { 8543d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard throw new UnsupportedOperationException("Request header queries do not support " 8553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard + "projections, selections or sorting"); 85657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 8573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard return queryRequestHeaders(db, uri); 8583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 8593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 860e610c0502c00689411624c00c3f81497df93b202Steve Howard SqlSelection fullSelection = getWhereClause(uri, selection, selectionArgs, match); 86157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 8625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard if (shouldRestrictVisibility()) { 8631fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (projection == null) { 8649b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey projection = sAppReadableColumnsArray.clone(); 8651fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } else { 86601d0182d86db003b2da5b831cb26820093888d9aVasu Nori // check the validity of the columns in projection 8671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project for (int i = 0; i < projection.length; ++i) { 86801d0182d86db003b2da5b831cb26820093888d9aVasu Nori if (!sAppReadableColumnsSet.contains(projection[i]) && 86901d0182d86db003b2da5b831cb26820093888d9aVasu Nori !downloadManagerColumnsList.contains(projection[i])) { 8701fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project throw new IllegalArgumentException( 8711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project "column " + projection[i] + " is not allowed in queries"); 8721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 8731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 8741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 8759b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey 8769b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey for (int i = 0; i < projection.length; i++) { 8779b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey final String newColumn = sColumnsMap.get(projection[i]); 8789b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey if (newColumn != null) { 8799b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey projection[i] = newColumn; 8809b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey } 8819b606340a0b5b88436505651dbe3cdaf60117604Jeff Sharkey } 88257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 88357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 88457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGVV) { 8853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard logVerboseQueryInfo(projection, selection, selectionArgs, sort, db); 88657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 88757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 888e610c0502c00689411624c00c3f81497df93b202Steve Howard Cursor ret = db.query(DB_TABLE, projection, fullSelection.getSelection(), 889e610c0502c00689411624c00c3f81497df93b202Steve Howard fullSelection.getParameters(), null, null, sort); 89057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 89157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (ret != null) { 89257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project ret.setNotificationUri(getContext().getContentResolver(), uri); 89357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGVV) { 89457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project Log.v(Constants.TAG, 89557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project "created cursor " + ret + " on behalf of " + Binder.getCallingPid()); 89657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 89757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } else { 89857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGV) { 8991fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Log.v(Constants.TAG, "query failed in downloads database"); 90057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 90157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 90257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 90357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project return ret; 90457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 90557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 9063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private void logVerboseQueryInfo(String[] projection, final String selection, 9073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard final String[] selectionArgs, final String sort, SQLiteDatabase db) { 9083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard java.lang.StringBuilder sb = new java.lang.StringBuilder(); 9093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("starting query, database is "); 9103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (db != null) { 9113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("not "); 9123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("null; "); 9143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (projection == null) { 9153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("projection is null; "); 9163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else if (projection.length == 0) { 9173d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("projection is empty; "); 9183d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 9193d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard for (int i = 0; i < projection.length; ++i) { 9203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("projection["); 9213d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(i); 9223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("] is "); 9233d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(projection[i]); 9243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("; "); 9253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9263d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("selection is "); 9283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(selection); 9293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("; "); 9303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (selectionArgs == null) { 9313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("selectionArgs is null; "); 9323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else if (selectionArgs.length == 0) { 9333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("selectionArgs is empty; "); 9343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 9353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard for (int i = 0; i < selectionArgs.length; ++i) { 9363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("selectionArgs["); 9373d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(i); 9383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("] is "); 9393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(selectionArgs[i]); 9403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("; "); 9413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9423d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("sort is "); 9443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append(sort); 9453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard sb.append("."); 9463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, sb.toString()); 9473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 9483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 9495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private String getDownloadIdFromUri(final Uri uri) { 9505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard return uri.getPathSegments().get(1); 9515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 9535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 9545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Insert request headers for a download into the DB. 9555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 9565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private void insertRequestHeaders(SQLiteDatabase db, long downloadId, ContentValues values) { 9575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard ContentValues rowValues = new ContentValues(); 9585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID, downloadId); 9595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard for (Map.Entry<String, Object> entry : values.valueSet()) { 9605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String key = entry.getKey(); 9615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) { 9625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String headerLine = entry.getValue().toString(); 9635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard if (!headerLine.contains(":")) { 9645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard throw new IllegalArgumentException("Invalid HTTP header line: " + headerLine); 9655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String[] parts = headerLine.split(":", 2); 9675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_HEADER, parts[0].trim()); 9685224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_VALUE, parts[1].trim()); 9695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.insert(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, null, rowValues); 9705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 9745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 9755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Handle a query for the custom request headers registered for a download. 9765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 9775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) { 9785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" 9795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard + getDownloadIdFromUri(uri); 9805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER, 9815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard Downloads.Impl.RequestHeaders.COLUMN_VALUE}; 98253356ad554f53a093434161cb494b8e818c294fbSteve Howard return db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where, 98353356ad554f53a093434161cb494b8e818c294fbSteve Howard null, null, null, null); 9845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 9865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 9875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard * Delete request headers for downloads matching the given query. 9885224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 9895224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) { 9905224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String[] projection = new String[] {Downloads.Impl._ID}; 991b06b739b078ce4b00600487cfec31659647bf31fSteve Howard Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null); 9925224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard try { 9935224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { 9945224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard long id = cursor.getLong(0); 9955224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id; 9965224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null); 9975224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 9985224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } finally { 9995224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard cursor.close(); 10005224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 10015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 10025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 10035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard /** 10043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard * @return true if we should restrict the columns readable by this caller 10055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard */ 10065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard private boolean shouldRestrictVisibility() { 10075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard int callingUid = Binder.getCallingUid(); 10085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard return Binder.getCallingPid() != Process.myPid() && 10095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard callingUid != mSystemUid && 101012c7bd488afe3a611d665e364e361f04f4a8c8d8Jeff Brown callingUid != mDefContainerUid; 10115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard } 10125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard 101357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 101457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Updates a row in the database 101557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 101657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 101757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public int update(final Uri uri, final ContentValues values, 101857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project final String where, final String[] whereArgs) { 10191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 10201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Helpers.validateSelection(where, sAppReadableColumnsSet); 10211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 102257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 102357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 102457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int count; 10251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project boolean startService = false; 10261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 1027e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori if (values.containsKey(Downloads.Impl.COLUMN_DELETED)) { 1028e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori if (values.getAsInteger(Downloads.Impl.COLUMN_DELETED) == 1) { 1029e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori // some rows are to be 'deleted'. need to start DownloadService. 1030e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori startService = true; 1031e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori } 1032e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori } 1033e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori 10341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project ContentValues filteredValues; 10351fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (Binder.getCallingPid() != Process.myPid()) { 10361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project filteredValues = new ContentValues(); 10377dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues); 10387dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues); 10397dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL); 10401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (i != null) { 10417dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i); 10421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project startService = true; 10431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 1044e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori 10457dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues); 10467dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues); 1047e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori copyString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, values, filteredValues); 10487dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues); 1049e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori copyInteger(Downloads.Impl.COLUMN_DELETED, values, filteredValues); 10501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } else { 10511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project filteredValues = values; 1052a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins String filename = values.getAsString(Downloads.Impl._DATA); 1053a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins if (filename != null) { 1054a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins Cursor c = query(uri, new String[] 1055a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins { Downloads.Impl.COLUMN_TITLE }, null, null, null); 10563398db8f3b195959faa2a7cf09918f364432ac28Steve Howard if (!c.moveToFirst() || c.getString(0).isEmpty()) { 10573398db8f3b195959faa2a7cf09918f364432ac28Steve Howard values.put(Downloads.Impl.COLUMN_TITLE, new File(filename).getName()); 1058a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins } 1059a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins c.close(); 1060a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins } 106171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard 106271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS); 106371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING; 1064d319729622da1893e895f2e35f41d01ecdca3705Steve Howard boolean isUserBypassingSizeLimit = 1065d319729622da1893e895f2e35f41d01ecdca3705Steve Howard values.containsKey(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT); 1066d319729622da1893e895f2e35f41d01ecdca3705Steve Howard if (isRestart || isUserBypassingSizeLimit) { 106771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard startService = true; 106871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard } 106957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 10703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 107157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int match = sURIMatcher.match(uri); 107257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project switch (match) { 10733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case MY_DOWNLOADS: 10743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case MY_DOWNLOADS_ID: 10753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case ALL_DOWNLOADS: 10763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case ALL_DOWNLOADS_ID: 1077e610c0502c00689411624c00c3f81497df93b202Steve Howard SqlSelection selection = getWhereClause(uri, where, whereArgs, match); 10781fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (filteredValues.size() > 0) { 1079e610c0502c00689411624c00c3f81497df93b202Steve Howard count = db.update(DB_TABLE, filteredValues, selection.getSelection(), 1080e610c0502c00689411624c00c3f81497df93b202Steve Howard selection.getParameters()); 10811fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } else { 10821fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project count = 0; 10831fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 108457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project break; 10853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 10863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard default: 10873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri); 108857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project throw new UnsupportedOperationException("Cannot update URI: " + uri); 108957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 10903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 10913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard notifyContentChanged(uri, match); 10921fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (startService) { 10931fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Context context = getContext(); 10941fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project context.startService(new Intent(context, DownloadService.class)); 10951fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 109657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project return count; 109757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 109857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 10993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard /** 11003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard * Notify of a change through both URIs (/my_downloads and /all_downloads) 11013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard * @param uri either URI for the changed download(s) 11023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard * @param uriMatch the match ID from {@link #sURIMatcher} 11033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard */ 11043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private void notifyContentChanged(final Uri uri, int uriMatch) { 11053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Long downloadId = null; 11063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) { 11073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard downloadId = Long.parseLong(getDownloadIdFromUri(uri)); 11083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 11093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard for (Uri uriToNotify : BASE_URIS) { 11103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (downloadId != null) { 11113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard uriToNotify = ContentUris.withAppendedId(uriToNotify, downloadId); 11123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 11133d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard getContext().getContentResolver().notifyChange(uriToNotify, null); 11143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 11153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 11163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 1117e610c0502c00689411624c00c3f81497df93b202Steve Howard private SqlSelection getWhereClause(final Uri uri, final String where, final String[] whereArgs, 1118e610c0502c00689411624c00c3f81497df93b202Steve Howard int uriMatch) { 1119e610c0502c00689411624c00c3f81497df93b202Steve Howard SqlSelection selection = new SqlSelection(); 1120e610c0502c00689411624c00c3f81497df93b202Steve Howard selection.appendClause(where, whereArgs); 11213ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID || 11223ca67748bc92eac89f731796c5597ff1fbe9217bVasu Nori uriMatch == PUBLIC_DOWNLOAD_ID) { 1123e610c0502c00689411624c00c3f81497df93b202Steve Howard selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri)); 1124b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 1125e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID) 1126e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root && getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ALL) 1127e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root != PackageManager.PERMISSION_GRANTED) { 1128e610c0502c00689411624c00c3f81497df93b202Steve Howard selection.appendClause( 1129e610c0502c00689411624c00c3f81497df93b202Steve Howard Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?", 1130bff4fe9b858d95e984298d4863a5199f1ee2c54eJeff Sharkey Binder.getCallingUid(), Binder.getCallingUid()); 1131b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 1132e610c0502c00689411624c00c3f81497df93b202Steve Howard return selection; 1133b06b739b078ce4b00600487cfec31659647bf31fSteve Howard } 1134b06b739b078ce4b00600487cfec31659647bf31fSteve Howard 113557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 113657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Deletes a row in the database 113757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 113857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 113957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project public int delete(final Uri uri, final String where, 114057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project final String[] whereArgs) { 11411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 11421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Helpers.validateSelection(where, sAppReadableColumnsSet); 11431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 114457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 114557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int count; 114657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project int match = sURIMatcher.match(uri); 114757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project switch (match) { 11483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case MY_DOWNLOADS: 11493d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case MY_DOWNLOADS_ID: 11503d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case ALL_DOWNLOADS: 11513d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard case ALL_DOWNLOADS_ID: 1152e610c0502c00689411624c00c3f81497df93b202Steve Howard SqlSelection selection = getWhereClause(uri, where, whereArgs, match); 1153e610c0502c00689411624c00c3f81497df93b202Steve Howard deleteRequestHeaders(db, selection.getSelection(), selection.getParameters()); 1154afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey 1155afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey final Cursor cursor = db.query(DB_TABLE, new String[] { 1156afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey Downloads.Impl._ID }, selection.getSelection(), selection.getParameters(), 1157afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey null, null, null); 1158afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey try { 1159afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey while (cursor.moveToNext()) { 1160afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey final long id = cursor.getLong(0); 1161afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey DownloadStorageProvider.onDownloadProviderDelete(getContext(), id); 1162afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey } 1163afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey } finally { 1164afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey IoUtils.closeQuietly(cursor); 1165afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey } 1166afaf53bd2b1322167b6f31eda941e38335c4a952Jeff Sharkey 1167e610c0502c00689411624c00c3f81497df93b202Steve Howard count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters()); 116857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project break; 11693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 11703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard default: 11713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri); 117257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project throw new UnsupportedOperationException("Cannot delete URI: " + uri); 117357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 11743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard notifyContentChanged(uri, match); 117557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project return count; 117657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 117771aab521efba9b28779541440c797220ec98ac97Steve Howard 117857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project /** 117957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Remotely opens a file 118057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */ 118157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project @Override 1182c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey public ParcelFileDescriptor openFile(final Uri uri, String mode) throws FileNotFoundException { 118357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project if (Constants.LOGVV) { 11843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard logVerboseOpenFileInfo(uri, mode); 118557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 11861fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 118704c1c2afb7481e7fb9b66f96023c79d19cad8c90Jeff Sharkey final Cursor cursor = query(uri, new String[] { Downloads.Impl._DATA }, null, null, null); 11883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard String path; 11893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard try { 11903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard int count = (cursor != null) ? cursor.getCount() : 0; 11913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (count != 1) { 11923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard // If there is not exactly one result, throw an appropriate exception. 11933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (count == 0) { 11943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard throw new FileNotFoundException("No entry for " + uri); 11953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 11963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard throw new FileNotFoundException("Multiple items at " + uri); 11971fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 11983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 11993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard cursor.moveToFirst(); 12003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard path = cursor.getString(0); 12013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } finally { 1202c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey IoUtils.closeQuietly(cursor); 12031fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 12041fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 12051fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (path == null) { 12061fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project throw new FileNotFoundException("No filename found."); 12071fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 12089aadb4b3f2b3c914166ebfae8851fbecaf536f4fVasu Nori if (!Helpers.isFilenameValid(path, mDownloadsDataDir)) { 1209b18ed519040c1ecd98f8cb139adcc315a3f4eedcVasu Nori throw new FileNotFoundException("Invalid filename: " + path); 12101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 12113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 1212c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey final File file = new File(path); 1213c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey if ("r".equals(mode)) { 121404c1c2afb7481e7fb9b66f96023c79d19cad8c90Jeff Sharkey return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); 1215c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey } else { 1216c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey try { 1217c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey // When finished writing, update size and timestamp 121804c1c2afb7481e7fb9b66f96023c79d19cad8c90Jeff Sharkey return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode), 1219c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey mHandler, new OnCloseListener() { 1220c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey @Override 1221c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey public void onClose(IOException e) { 1222c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey final ContentValues values = new ContentValues(); 1223c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, file.length()); 1224c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey values.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, 1225c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey System.currentTimeMillis()); 1226c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey update(uri, values, null, null); 1227c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey } 1228c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey }); 1229c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey } catch (IOException e) { 1230c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey throw new FileNotFoundException("Failed to open for writing: " + e); 1231c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey } 1232c067c8be21dc4a6dc5f49b2b1aed7f91aab47063Jeff Sharkey } 123357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project } 123457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project 12351d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey @Override 12361d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 12371d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 120); 12381d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey 12391d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.println("Downloads updated in last hour:"); 12401d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.increaseIndent(); 12411d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey 12421d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 12431d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final long modifiedAfter = mSystemFacade.currentTimeMillis() - DateUtils.HOUR_IN_MILLIS; 12441d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final Cursor cursor = db.query(DB_TABLE, null, 12451d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey Downloads.Impl.COLUMN_LAST_MODIFICATION + ">" + modifiedAfter, null, null, null, 12461d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey Downloads.Impl._ID + " ASC"); 12471d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey try { 12481d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final String[] cols = cursor.getColumnNames(); 12491d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey final int idCol = cursor.getColumnIndex(BaseColumns._ID); 12501d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey while (cursor.moveToNext()) { 12511d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.println("Download #" + cursor.getInt(idCol) + ":"); 12521d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.increaseIndent(); 12531d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey for (int i = 0; i < cols.length; i++) { 12541d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey // Omit sensitive data when dumping 12551d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey if (Downloads.Impl.COLUMN_COOKIE_DATA.equals(cols[i])) { 12561d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey continue; 12571d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } 12581d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.printPair(cols[i], cursor.getString(i)); 12591d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } 12601d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.println(); 12611d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.decreaseIndent(); 12621d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } 12631d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } finally { 12641d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey cursor.close(); 12651d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } 12661d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey 12671d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey pw.decreaseIndent(); 12681d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey } 12691d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey 12703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard private void logVerboseOpenFileInfo(Uri uri, String mode) { 12713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode 12723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard + ", uid: " + Binder.getCallingUid()); 12733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Cursor cursor = query(Downloads.Impl.CONTENT_URI, 12743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard new String[] { "_id" }, null, null, "_id"); 12753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (cursor == null) { 12763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "null cursor in openFile"); 12773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 12783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (!cursor.moveToFirst()) { 12793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "empty cursor in openFile"); 12803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 12813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard do { 12823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available"); 12833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } while(cursor.moveToNext()); 12843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 12853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard cursor.close(); 12863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 12873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard cursor = query(uri, new String[] { "_data" }, null, null, null); 12883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (cursor == null) { 12893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "null cursor in openFile"); 12903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 12913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (!cursor.moveToFirst()) { 12923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "empty cursor in openFile"); 12933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } else { 12943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard String filename = cursor.getString(0); 12953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "filename in openFile: " + filename); 12963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard if (new java.io.File(filename).isFile()) { 12973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard Log.v(Constants.TAG, "file exists in openFile"); 12983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 12993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 13001d0a0aa2cc5bfed8107aa70f7e890fde9a7ea2b4Jeff Sharkey cursor.close(); 13013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 13023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard } 13033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard 13041fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project private static final void copyInteger(String key, ContentValues from, ContentValues to) { 13051fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Integer i = from.getAsInteger(key); 13061fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (i != null) { 13071fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project to.put(key, i); 13081fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13091fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 13111fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project private static final void copyBoolean(String key, ContentValues from, ContentValues to) { 13121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project Boolean b = from.getAsBoolean(key); 13131fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (b != null) { 13141fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project to.put(key, b); 13151fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13161fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13171fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 13181fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project private static final void copyString(String key, ContentValues from, ContentValues to) { 13191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project String s = from.getAsString(key); 13201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project if (s != null) { 13211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project to.put(key, s); 13221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13231fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project } 13241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project 1325a89321ea04ced76d06f60f5909be203cb654a830Steve Howard private static final void copyStringWithDefault(String key, ContentValues from, 1326a89321ea04ced76d06f60f5909be203cb654a830Steve Howard ContentValues to, String defaultValue) { 1327a89321ea04ced76d06f60f5909be203cb654a830Steve Howard copyString(key, from, to); 1328a89321ea04ced76d06f60f5909be203cb654a830Steve Howard if (!to.containsKey(key)) { 1329a89321ea04ced76d06f60f5909be203cb654a830Steve Howard to.put(key, defaultValue); 1330a89321ea04ced76d06f60f5909be203cb654a830Steve Howard } 1331a89321ea04ced76d06f60f5909be203cb654a830Steve Howard } 133257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project} 1333