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