DownloadProvider.java revision e1823c84698006aa26a8c5dcfa5c4034858dfbe3
157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/*
257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * you may not use this file except in compliance with the License.
657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * You may obtain a copy of the License at
757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project *
1057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * See the License for the specific language governing permissions and
1457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * limitations under the License.
1557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
1657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpackage com.android.providers.downloads;
1857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentProvider;
203d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howardimport android.content.ContentUris;
2157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.ContentValues;
2257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Context;
2357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.Intent;
2457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.UriMatcher;
2591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.ApplicationInfo;
2657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.content.pm.PackageManager;
2791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapuimport android.content.pm.PackageManager.NameNotFoundException;
281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CrossProcessCursor;
2957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.Cursor;
301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CursorWindow;
311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport android.database.CursorWrapper;
32c6f5aad265cfc36a64cd2bdb5adf3cc9736bbd80Jean-Baptiste Queruimport android.database.SQLException;
3357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
3457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
3557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.net.Uri;
3657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Binder;
37b06b739b078ce4b00600487cfec31659647bf31fSteve Howardimport android.os.Environment;
3857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.ParcelFileDescriptor;
3957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.os.Process;
4057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.provider.Downloads;
4157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport android.util.Log;
4257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport com.google.common.annotations.VisibleForTesting;
445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.io.File;
4657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectimport java.io.FileNotFoundException;
47e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.ArrayList;
481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Projectimport java.util.HashSet;
49e61798da80558450f580ed948d0d469bd6423d8eSteve Howardimport java.util.Iterator;
50e610c0502c00689411624c00c3f81497df93b202Steve Howardimport java.util.List;
515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howardimport java.util.Map;
52e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Noriimport java.util.Set;
531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
5457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project/**
5657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project * Allows application to interact with the download manager.
5757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project */
5857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Projectpublic final class DownloadProvider extends ContentProvider {
5957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Database filename */
6057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_NAME = "downloads.db";
611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    /** Current database version */
62e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori    private static final int DB_VERSION = 106;
6357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** Name of table in the database */
6457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DB_TABLE = "downloads";
6557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
6657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for the entire download list */
6757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_LIST_TYPE = "vnd.android.cursor.dir/download";
6857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** MIME type for an individual download */
6957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final String DOWNLOAD_TYPE = "vnd.android.cursor.item/download";
7057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher used to recognize URIs sent by applications */
7257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads belonging to the calling UID */
743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS = 1;
753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of an individual download belonging to the calling UID */
763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int MY_DOWNLOADS_ID = 2;
773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** URI matcher constant for the URI of all downloads in the system */
783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS = 3;
7957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** URI matcher constant for the URI of an individual download */
803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int ALL_DOWNLOADS_ID = 4;
815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /** URI matcher constant for the URI of a download's request headers */
823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final int REQUEST_HEADERS_URI = 5;
8357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    static {
843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads", MY_DOWNLOADS);
853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "my_downloads/#", MY_DOWNLOADS_ID);
863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads", ALL_DOWNLOADS);
873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads", "all_downloads/#", ALL_DOWNLOADS_ID);
883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "my_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sURIMatcher.addURI("downloads",
923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                "all_downloads/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                REQUEST_HEADERS_URI);
944bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        // temporary, for backwards compatibility
954bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download", MY_DOWNLOADS);
964bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads", "download/#", MY_DOWNLOADS_ID);
974bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard        sURIMatcher.addURI("downloads",
984bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                "download/#/" + Downloads.Impl.RequestHeaders.URI_SEGMENT,
994bebe75b3e2361d7fb0aa966598c41c45ad9317fSteve Howard                REQUEST_HEADERS_URI);
10057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
10157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
1023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /** Different base URIs that could be used to access an individual download */
1033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private static final Uri[] BASE_URIS = new Uri[] {
1043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.CONTENT_URI,
1053d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
1063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    };
1073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
1081fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final String[] sAppReadableColumnsArray = new String[] {
1097dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._ID,
1107dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_APP_DATA,
1117dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl._DATA,
1127dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_MIME_TYPE,
1137dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_VISIBILITY,
1147dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_DESTINATION,
1157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CONTROL,
1167dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_STATUS,
1177dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_LAST_MODIFICATION,
1187dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
1197dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
1207dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TOTAL_BYTES,
1217dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_CURRENT_BYTES,
1227dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Downloads.Impl.COLUMN_TITLE,
1230a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        Downloads.Impl.COLUMN_DESCRIPTION,
1240d8d89105c00edbad95a268aaae65f2ff94ed5a1Steve Howard        Downloads.Impl.COLUMN_URI,
12571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
126b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        Downloads.Impl.COLUMN_FILE_NAME_HINT,
127e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
128e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        Downloads.Impl.COLUMN_DELETED,
1291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    };
1301fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1311fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static HashSet<String> sAppReadableColumnsSet;
1321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    static {
1331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        sAppReadableColumnsSet = new HashSet<String>();
1341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        for (int i = 0; i < sAppReadableColumnsArray.length; ++i) {
1351fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            sAppReadableColumnsSet.add(sAppReadableColumnsArray[i]);
1361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
1371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
1381fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
13957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /** The database that lies underneath this content provider */
14057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private SQLiteOpenHelper mOpenHelper = null;
14157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
14291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    /** List of uids that can access the downloads */
14391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mSystemUid = -1;
14491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu    private int mDefContainerUid = -1;
14591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu
1466d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    @VisibleForTesting
1476d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard    SystemFacade mSystemFacade;
1486d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
14957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
150e610c0502c00689411624c00c3f81497df93b202Steve Howard     * This class encapsulates a SQL where clause and its parameters.  It makes it possible for
151e610c0502c00689411624c00c3f81497df93b202Steve Howard     * shared methods (like {@link DownloadProvider#getWhereClause(Uri, String, String[], int)})
152e610c0502c00689411624c00c3f81497df93b202Steve Howard     * to return both pieces of information, and provides some utility logic to ease piece-by-piece
153e610c0502c00689411624c00c3f81497df93b202Steve Howard     * construction of selections.
154e610c0502c00689411624c00c3f81497df93b202Steve Howard     */
155e610c0502c00689411624c00c3f81497df93b202Steve Howard    private static class SqlSelection {
156e610c0502c00689411624c00c3f81497df93b202Steve Howard        public StringBuilder mWhereClause = new StringBuilder();
157e610c0502c00689411624c00c3f81497df93b202Steve Howard        public List<String> mParameters = new ArrayList<String>();
158e610c0502c00689411624c00c3f81497df93b202Steve Howard
159e610c0502c00689411624c00c3f81497df93b202Steve Howard        public <T> void appendClause(String newClause, final T... parameters) {
160e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (newClause == null || newClause.isEmpty()) {
161e610c0502c00689411624c00c3f81497df93b202Steve Howard                return;
162e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
163e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (mWhereClause.length() != 0) {
164e610c0502c00689411624c00c3f81497df93b202Steve Howard                mWhereClause.append(" AND ");
165e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
166e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append("(");
167e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(newClause);
168e610c0502c00689411624c00c3f81497df93b202Steve Howard            mWhereClause.append(")");
169e610c0502c00689411624c00c3f81497df93b202Steve Howard            if (parameters != null) {
170e610c0502c00689411624c00c3f81497df93b202Steve Howard                for (Object parameter : parameters) {
171e610c0502c00689411624c00c3f81497df93b202Steve Howard                    mParameters.add(parameter.toString());
172e610c0502c00689411624c00c3f81497df93b202Steve Howard                }
173e610c0502c00689411624c00c3f81497df93b202Steve Howard            }
174e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
175e610c0502c00689411624c00c3f81497df93b202Steve Howard
176e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String getSelection() {
177e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mWhereClause.toString();
178e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
179e610c0502c00689411624c00c3f81497df93b202Steve Howard
180e610c0502c00689411624c00c3f81497df93b202Steve Howard        public String[] getParameters() {
181e610c0502c00689411624c00c3f81497df93b202Steve Howard            String[] array = new String[mParameters.size()];
182e610c0502c00689411624c00c3f81497df93b202Steve Howard            return mParameters.toArray(array);
183e610c0502c00689411624c00c3f81497df93b202Steve Howard        }
184e610c0502c00689411624c00c3f81497df93b202Steve Howard    }
185e610c0502c00689411624c00c3f81497df93b202Steve Howard
186e610c0502c00689411624c00c3f81497df93b202Steve Howard    /**
18757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Creates and updated database on demand when opening it.
18857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Helper class to create database the first time the provider is
18957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * initialized and upgrade it when a new version of the provider needs
19057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * an updated version of the database.
19157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
19257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    private final class DatabaseHelper extends SQLiteOpenHelper {
19357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public DatabaseHelper(final Context context) {
19457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            super(context, DB_NAME, null, DB_VERSION);
19557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
19657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
19757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
19857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Creates database the first time we try to open it.
19957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
20057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
20157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
20257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
20357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                Log.v(Constants.TAG, "populating new database");
20457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
2055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            onUpgrade(db, 0, DB_VERSION);
20657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
20757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
20857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        /**
20957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * Updates the database format when a content provider is used
21057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         * with a database that was created with a different format.
2115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         *
2125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Note: to support downgrades, creating a table should always drop it first if it already
2135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * exists.
21457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project         */
21557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        @Override
2161fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) {
2175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (oldV == 31) {
2185224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // 31 and 100 are identical, just in different codelines. Upgrading from 31 is the
2195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // same as upgrading from 100.
2205224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 100;
2215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV < 100) {
2225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // no logic to upgrade from these older version, just recreate the DB
2235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.i(Constants.TAG, "Upgrading downloads database from version " + oldV
2245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " to version " + newV + ", which will destroy all old data");
2255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } else if (oldV > newV) {
2275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // user must have downgraded software; we have no way to know how to downgrade the
2285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                // DB, so just recreate it
2295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.i(Constants.TAG, "Downgrading downloads database from version " + oldV
2305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                      + " (current version is " + newV + "), destroying all old data");
2315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                oldV = 99;
2325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (int version = oldV + 1; version <= newV; version++) {
2355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                upgradeTo(db, version);
2365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2395224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
2405224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Upgrade database from (version - 1) to version.
2415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
2425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void upgradeTo(SQLiteDatabase db, int version) {
2435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            switch (version) {
2445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 100:
2455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createDownloadsTable(db);
2465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                case 101:
2495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    createHeadersTable(db);
2505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    break;
2515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2520a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                case 102:
2530a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_PUBLIC_API,
2540a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2550a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOW_ROAMING,
2560a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2570a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES,
2580a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                              "INTEGER NOT NULL DEFAULT 0");
2590a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    break;
2600a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
26171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                case 103:
26271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI,
26371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                              "INTEGER NOT NULL DEFAULT 1");
26471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    makeCacheDownloadsInvisible(db);
26571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    break;
26671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
267d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                case 104:
268d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT,
269d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                            "INTEGER NOT NULL DEFAULT 0");
270d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                    break;
271d319729622da1893e895f2e35f41d01ecdca3705Steve Howard
27273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                case 105:
27373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    fillNullValues(db);
27473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard                    break;
27573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
276e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                case 106:
277e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, "TEXT");
278e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    addColumn(db, DB_TABLE, Downloads.Impl.COLUMN_DELETED,
279e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                            "BOOLEAN NOT NULL DEFAULT 0");
280e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                    break;
281e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
2825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                default:
2835224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalStateException("Don't know how to upgrade to " + version);
2845224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
2855224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
2865224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
2875224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        /**
28873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * insert() now ensures these four columns are never null for new downloads, so this method
28973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         * makes that true for existing columns, so that code can rely on this assumption.
29073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard         */
29173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValues(SQLiteDatabase db) {
29273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            ContentValues values = new ContentValues();
29373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
29473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
29573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
29673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
29773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_TITLE, "");
29873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
29973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.put(Downloads.Impl.COLUMN_DESCRIPTION, "");
30073f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            fillNullValuesForColumn(db, values);
30173f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
30273f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
30373f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        private void fillNullValuesForColumn(SQLiteDatabase db, ContentValues values) {
30473f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            String column = values.valueSet().iterator().next().getKey();
30573f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            db.update(DB_TABLE, values, column + " is null", null);
30673f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard            values.clear();
30773f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        }
30873f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard
30973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        /**
31071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         * Set all existing downloads to the cache partition to be invisible in the downloads UI.
31171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard         */
31271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        private void makeCacheDownloadsInvisible(SQLiteDatabase db) {
31371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            ContentValues values = new ContentValues();
31471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false);
31571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            String cacheSelection = Downloads.Impl.COLUMN_DESTINATION
31671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                    + " != " + Downloads.Impl.DESTINATION_EXTERNAL;
31771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            db.update(DB_TABLE, values, cacheSelection, null);
31871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
31971e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
32071e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        /**
3210a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * Add a column to a table using ALTER TABLE.
3220a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param dbTable name of the table
3230a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnName name of the column to add
3240a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         * @param columnDefinition SQL for the column definition
3250a77c62a82503b38c484e0079648f0231dd85d53Steve Howard         */
3260a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        private void addColumn(SQLiteDatabase db, String dbTable, String columnName,
3270a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                               String columnDefinition) {
3280a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            db.execSQL("ALTER TABLE " + dbTable + " ADD COLUMN " + columnName + " "
3290a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                       + columnDefinition);
3300a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
3310a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
3320a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        /**
3335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         * Creates the table that'll hold the download information.
3345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard         */
3355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createDownloadsTable(SQLiteDatabase db) {
3365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            try {
3375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
3385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.execSQL("CREATE TABLE " + DB_TABLE + "(" +
3395224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
3405224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_URI + " TEXT, " +
3415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, " +
3425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_APP_DATA + " TEXT, " +
3435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NO_INTEGRITY + " BOOLEAN, " +
3445224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_FILE_NAME_HINT + " TEXT, " +
3455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.OTA_UPDATE + " BOOLEAN, " +
3465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl._DATA + " TEXT, " +
3475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_MIME_TYPE + " TEXT, " +
3485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESTINATION + " INTEGER, " +
3495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.NO_SYSTEM_FILES + " BOOLEAN, " +
3505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_VISIBILITY + " INTEGER, " +
3515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CONTROL + " INTEGER, " +
3525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_STATUS + " INTEGER, " +
3535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.FAILED_CONNECTIONS + " INTEGER, " +
3545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_LAST_MODIFICATION + " BIGINT, " +
3555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + " TEXT, " +
3565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS + " TEXT, " +
3575224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS + " TEXT, " +
3585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_COOKIE_DATA + " TEXT, " +
3595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_USER_AGENT + " TEXT, " +
3605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_REFERER + " TEXT, " +
3615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TOTAL_BYTES + " INTEGER, " +
3625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_CURRENT_BYTES + " INTEGER, " +
3635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.ETAG + " TEXT, " +
3645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Constants.UID + " INTEGER, " +
3655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_OTHER_UID + " INTEGER, " +
3665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_TITLE + " TEXT, " +
3675224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                        Downloads.Impl.COLUMN_DESCRIPTION + " TEXT, " +
368a53c21edb5dc57d97dcddd03fbfa2022abf43787Vasu Nori                        Constants.MEDIA_SCANNED + " BOOLEAN);");
3695224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            } catch (SQLException ex) {
3705224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Log.e(Constants.TAG, "couldn't create table in downloads database");
3715224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                throw ex;
3725224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
3735224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
3745224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
3755224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        private void createHeadersTable(SQLiteDatabase db) {
3765224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("DROP TABLE IF EXISTS " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE);
3775224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            db.execSQL("CREATE TABLE " + Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE + "(" +
3785224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       "id INTEGER PRIMARY KEY AUTOINCREMENT," +
3795224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL," +
3805224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL," +
3815224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       Downloads.Impl.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL" +
3825224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       ");");
38357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
38457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
38557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
38657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
38757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Initializes the content provider when it is created.
38857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
38957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
39057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public boolean onCreate() {
3916d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        if (mSystemFacade == null) {
392af28400b74de05862b470412a5c92f68e99f59f8Steve Howard            mSystemFacade = new RealSystemFacade(getContext());
3936d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        }
3946d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard
39557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        mOpenHelper = new DatabaseHelper(getContext());
39691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the system uid
39791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        mSystemUid = Process.SYSTEM_UID;
39891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // Initialize the default container uid. Package name hardcoded
39991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        // for now.
40091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        ApplicationInfo appInfo = null;
40191e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        try {
40291e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            appInfo = getContext().getPackageManager().
40391e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu                    getApplicationInfo("com.android.defcontainer", 0);
40491e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        } catch (NameNotFoundException e) {
40591e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            // TODO Auto-generated catch block
40691e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            e.printStackTrace();
40791e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
40891e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        if (appInfo != null) {
40991e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu            mDefContainerUid = appInfo.uid;
41091e4522fa90d969a596058756c24e173df1a6196Suchi Amalapurapu        }
41157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return true;
41257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
41357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
41457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
41557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Returns the content-provider-style MIME types of the various
41657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * types accessible through this content provider.
41757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
41857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
41957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public String getType(final Uri uri) {
42057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
42157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
4223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS: {
42357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_LIST_TYPE;
42457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
4253d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID: {
42657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                return DOWNLOAD_TYPE;
42757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
42857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            default: {
42957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                if (Constants.LOGV) {
43057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                    Log.v(Constants.TAG, "calling getType on an unknown URI: " + uri);
43157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                }
43257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new IllegalArgumentException("Unknown URI: " + uri);
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    /**
43857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Inserts a row in the database
43957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
44057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
44157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public Uri insert(final Uri uri, final ContentValues values) {
442b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        checkInsertPermissions(values);
44357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
44457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
4453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        // note we disallow inserting into ALL_DOWNLOADS
4463d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        int match = sURIMatcher.match(uri);
4473d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match != MY_DOWNLOADS) {
4483d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri);
44957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
45057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
45157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
4521fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues = new ContentValues();
4531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
4547dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_URI, values, filteredValues);
4557dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
4567dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues);
4577dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues);
4587dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues);
45971aab521efba9b28779541440c797220ec98ac97Steve Howard
46071aab521efba9b28779541440c797220ec98ac97Steve Howard        copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues);
46171aab521efba9b28779541440c797220ec98ac97Steve Howard        boolean isPublicApi =
46271aab521efba9b28779541440c797220ec98ac97Steve Howard                values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE;
46371aab521efba9b28779541440c797220ec98ac97Steve Howard
4647dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION);
465ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (dest != null) {
4667dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
4671fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    != PackageManager.PERMISSION_GRANTED
4687dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    && dest != Downloads.Impl.DESTINATION_EXTERNAL
4696d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                    && dest != Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
4706d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                    && dest != Downloads.Impl.DESTINATION_FILE_URI) {
4711fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                throw new SecurityException("unauthorized destination code");
4721fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
47371aab521efba9b28779541440c797220ec98ac97Steve Howard            // for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically
47471aab521efba9b28779541440c797220ec98ac97Steve Howard            // switch to non-purgeable download
47571aab521efba9b28779541440c797220ec98ac97Steve Howard            boolean hasNonPurgeablePermission =
47671aab521efba9b28779541440c797220ec98ac97Steve Howard                    getContext().checkCallingPermission(
47771aab521efba9b28779541440c797220ec98ac97Steve Howard                            Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE)
47871aab521efba9b28779541440c797220ec98ac97Steve Howard                            == PackageManager.PERMISSION_GRANTED;
47971aab521efba9b28779541440c797220ec98ac97Steve Howard            if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE
48071aab521efba9b28779541440c797220ec98ac97Steve Howard                    && hasNonPurgeablePermission) {
48171aab521efba9b28779541440c797220ec98ac97Steve Howard                dest = Downloads.Impl.DESTINATION_CACHE_PARTITION;
48271aab521efba9b28779541440c797220ec98ac97Steve Howard            }
4836d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            if (dest == Downloads.Impl.DESTINATION_FILE_URI) {
4846d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                getContext().enforcePermission(
4856d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
4866d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        Binder.getCallingPid(), Binder.getCallingUid(),
4876d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                        "need WRITE_EXTERNAL_STORAGE permission to use DESTINATION_FILE_URI");
488b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                checkFileUriDestination(values);
4896d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard            }
4907dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);
491ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        }
4927dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        Integer vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY);
493ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        if (vis == null) {
4947dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {
4957dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
4967dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
497ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru            } else {
4987dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,
4997dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        Downloads.Impl.VISIBILITY_HIDDEN);
5001fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
501ce8813afb256269e9e223f72ebced92560201bbbJean-Baptiste Queru        } else {
5027dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis);
50357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
5047dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
5057dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
5066d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard        filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION,
5076d9b98282c817b86a00f9c19a705da4cb19bc3a6Steve Howard                           mSystemFacade.currentTimeMillis());
5080a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
5097dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);
5107dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);
5110a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (pckg != null && (clazz != null || isPublicApi)) {
5121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            int uid = Binder.getCallingUid();
5131fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            try {
5140a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) {
5157dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                    filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg);
5160a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    if (clazz != null) {
5170a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                        filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz);
5180a77c62a82503b38c484e0079648f0231dd85d53Steve Howard                    }
5191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
5201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } catch (PackageManager.NameNotFoundException ex) {
5211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                /* ignored for now */
52257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
52357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
5247dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues);
5257dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues);
5267dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues);
5277dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues);
5287dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru        if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)
5291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                == PackageManager.PERMISSION_GRANTED) {
5307dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues);
53157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
5321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        filteredValues.put(Constants.UID, Binder.getCallingUid());
5331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingUid() == 0) {
5341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            copyInteger(Constants.UID, values, filteredValues);
53557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
536a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, "");
537a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, "");
538b5629da794cb3c1ca1970d206343743b165b9644Steve Howard        filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
53973f5f223477795e10079d25c1eb5f796af1f00a9Steve Howard        filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
54057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
54171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        if (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) {
54271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues);
54371e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        } else {
54471e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            // by default, make external downloads visible in the UI
54571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL);
54671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal);
54771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        }
54871e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
5490a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        if (isPublicApi) {
5500a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);
5510a77c62a82503b38c484e0079648f0231dd85d53Steve Howard            copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);
5520a77c62a82503b38c484e0079648f0231dd85d53Steve Howard        }
5530a77c62a82503b38c484e0079648f0231dd85d53Steve Howard
5541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Constants.LOGVV) {
5551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            Log.v(Constants.TAG, "initiating download with UID "
5561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    + filteredValues.getAsInteger(Constants.UID));
5577dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) {
5581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "other UID " +
5597dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                        filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID));
5601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
56157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
56257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
56357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        Context context = getContext();
56457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        context.startService(new Intent(context, DownloadService.class));
56557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5661fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        long rowID = db.insert(DB_TABLE, null, filteredValues);
5673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (rowID == -1) {
5683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.d(Constants.TAG, "couldn't insert into downloads database");
5693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return null;
57057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
57157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
5723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        insertRequestHeaders(db, rowID, values);
5733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        context.startService(new Intent(context, DownloadService.class));
5743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
5753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
57657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
57757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
57857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
579b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Check that the file URI provided for DESTINATION_FILE_URI is valid.
580b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
581b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkFileUriDestination(ContentValues values) {
582b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String fileUri = values.getAsString(Downloads.Impl.COLUMN_FILE_NAME_HINT);
583b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (fileUri == null) {
584b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException(
585b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    "DESTINATION_FILE_URI must include a file URI under COLUMN_FILE_NAME_HINT");
586b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
587b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Uri uri = Uri.parse(fileUri);
5885d81e2447ed77860afecd71583e137178c2c6807Steve Howard        String scheme = uri.getScheme();
5895d81e2447ed77860afecd71583e137178c2c6807Steve Howard        if (scheme == null || !scheme.equals("file")) {
590b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new IllegalArgumentException("Not a file URI: " + uri);
591b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
592b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        String path = uri.getPath();
593b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        if (path == null) {
594b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard            throw new IllegalArgumentException("Invalid file URI: " + uri);
595b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        }
596b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath();
597b9a0ad7182209d4aca708e13e876e9b1b43ffafcSteve Howard        if (!path.startsWith(externalPath)) {
598b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException("Destination must be on external storage: " + uri);
599b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
600b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
601b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
602b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
603b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Apps with the ACCESS_DOWNLOAD_MANAGER permission can access this provider freely, subject to
604b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * constraints in the rest of the code. Apps without that may still access this provider through
605b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * the public API, but additional restrictions are imposed. We check those restrictions here.
606b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     *
607b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @param values ContentValues provided to insert()
608b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * @throws SecurityException if the caller has insufficient permissions
609b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
610b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void checkInsertPermissions(ContentValues values) {
611b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_ACCESS)
612b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                == PackageManager.PERMISSION_GRANTED) {
613b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            return;
614b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
615b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
616b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET,
617b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                "INTERNET permission is required to use the download manager");
618b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
619b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // ensure the request fits within the bounds of a public API request
620b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // first copy so we can remove values
621b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values = new ContentValues(values);
622b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
623b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // check columns whose values are restricted
624b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_IS_PUBLIC_API, Boolean.TRUE);
625b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION,
626b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE,
627b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                Downloads.Impl.DESTINATION_FILE_URI);
6289da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard
6299da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION)
6309da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                == PackageManager.PERMISSION_GRANTED) {
6319da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
6329da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                    Downloads.Impl.VISIBILITY_HIDDEN, Downloads.Impl.VISIBILITY_VISIBLE);
6339da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        } else {
6349da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
6359da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard                    Downloads.Impl.VISIBILITY_VISIBLE);
6369da9df3d6e84a3c4b04dd22d277e0e6d8f7f1ccbSteve Howard        }
637b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
638b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // remove the rest of the columns that are allowed (with any value)
639b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_URI);
640b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_TITLE);
641b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_DESCRIPTION);
642b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_MIME_TYPE);
643b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_FILE_NAME_HINT); // checked later in insert()
644b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE); // checked later in insert()
645b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES);
646b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(Downloads.Impl.COLUMN_ALLOW_ROAMING);
64771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard        values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI);
648e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        Iterator<Map.Entry<String, Object>> iterator = values.valueSet().iterator();
649e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        while (iterator.hasNext()) {
650e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            String key = iterator.next().getKey();
651e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
652e61798da80558450f580ed948d0d469bd6423d8eSteve Howard                iterator.remove();
653e61798da80558450f580ed948d0d469bd6423d8eSteve Howard            }
654e61798da80558450f580ed948d0d469bd6423d8eSteve Howard        }
655b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
656b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        // any extra columns are extraneous and disallowed
657b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        if (values.size() > 0) {
658b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            StringBuilder error = new StringBuilder("Invalid columns in request: ");
659b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            boolean first = true;
660b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            for (Map.Entry<String, Object> entry : values.valueSet()) {
661b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                if (!first) {
662b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                    error.append(", ");
663b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                }
664b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                error.append(entry.getKey());
665b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
666b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            throw new SecurityException(error.toString());
667b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
668b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
669b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
670b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
671b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * Remove column from values, and throw a SecurityException if the value isn't within the
672b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     * specified allowedValues.
673b06b739b078ce4b00600487cfec31659647bf31fSteve Howard     */
674b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    private void enforceAllowedValues(ContentValues values, String column,
675b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            Object... allowedValues) {
676b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Object value = values.get(column);
677b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        values.remove(column);
678b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        for (Object allowedValue : allowedValues) {
679b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value == null && allowedValue == null) {
680b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
681b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
682b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            if (value != null && value.equals(allowedValue)) {
683b06b739b078ce4b00600487cfec31659647bf31fSteve Howard                return;
684b06b739b078ce4b00600487cfec31659647bf31fSteve Howard            }
685b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
686b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        throw new SecurityException("Invalid value for " + column + ": " + value);
687b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
688b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
689b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    /**
69057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Starts a database query
69157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
69257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
6931fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    public Cursor query(final Uri uri, String[] projection,
69457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String selection, final String[] selectionArgs,
69557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project             final String sort) {
6961fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
6971fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(selection, sAppReadableColumnsSet);
6981fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
69957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
70057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
70157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
7023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == -1) {
7033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (Constants.LOGV) {
7043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "querying unknown URI: " + uri);
70557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
7063d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            throw new IllegalArgumentException("Unknown URI: " + uri);
7073d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7083d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
7093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (match == REQUEST_HEADERS_URI) {
7103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (projection != null || selection != null || sort != null) {
7113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new UnsupportedOperationException("Request header queries do not support "
7123d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                                                        + "projections, selections or sorting");
71357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
7143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            return queryRequestHeaders(db, uri);
7153d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7163d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
717e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection fullSelection = getWhereClause(uri, selection, selectionArgs, match);
71857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        if (shouldRestrictVisibility()) {
7201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (projection == null) {
7211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                projection = sAppReadableColumnsArray;
7221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            } else {
7231fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                for (int i = 0; i < projection.length; ++i) {
7241fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    if (!sAppReadableColumnsSet.contains(projection[i])) {
7251fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                        throw new IllegalArgumentException(
7261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                                "column " + projection[i] + " is not allowed in queries");
7271fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    }
7281fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
7291fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
73057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
73157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
73257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
7333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseQueryInfo(projection, selection, selectionArgs, sort, db);
73457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
73557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
736e610c0502c00689411624c00c3f81497df93b202Steve Howard        Cursor ret = db.query(DB_TABLE, projection, fullSelection.getSelection(),
737e610c0502c00689411624c00c3f81497df93b202Steve Howard                fullSelection.getParameters(), null, null, sort);
73857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
73957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret != null) {
7401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project           ret = new ReadOnlyCursorWrapper(ret);
7411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
7421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
7431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (ret != null) {
74457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            ret.setNotificationUri(getContext().getContentResolver(), uri);
74557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGVV) {
74657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                Log.v(Constants.TAG,
74757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                        "created cursor " + ret + " on behalf of " + Binder.getCallingPid());
74857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
74957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        } else {
75057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            if (Constants.LOGV) {
7511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "query failed in downloads database");
75257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
75357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
75457f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
75557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
75657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
75757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
7583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseQueryInfo(String[] projection, final String selection,
7593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            final String[] selectionArgs, final String sort, SQLiteDatabase db) {
7603d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        java.lang.StringBuilder sb = new java.lang.StringBuilder();
7613d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("starting query, database is ");
7623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (db != null) {
7633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("not ");
7643d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7653d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("null; ");
7663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (projection == null) {
7673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is null; ");
7683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (projection.length == 0) {
7693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("projection is empty; ");
7703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
7713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < projection.length; ++i) {
7723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("projection[");
7733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
7743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
7753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(projection[i]);
7763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
7773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
7783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("selection is ");
7803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(selection);
7813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("; ");
7823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (selectionArgs == null) {
7833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is null; ");
7843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else if (selectionArgs.length == 0) {
7853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            sb.append("selectionArgs is empty; ");
7863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
7873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            for (int i = 0; i < selectionArgs.length; ++i) {
7883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("selectionArgs[");
7893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(i);
7903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("] is ");
7913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append(selectionArgs[i]);
7923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                sb.append("; ");
7933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
7943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
7953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append("sort is ");
7963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(sort);
7973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        sb.append(".");
7983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Log.v(Constants.TAG, sb.toString());
7993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
8003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
8015224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private String getDownloadIdFromUri(final Uri uri) {
8025224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return uri.getPathSegments().get(1);
8035224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
8045224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
8055224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
8065224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Insert request headers for a download into the DB.
8075224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
8085224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void insertRequestHeaders(SQLiteDatabase db, long downloadId, ContentValues values) {
8095224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        ContentValues rowValues = new ContentValues();
8105224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID, downloadId);
8115224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        for (Map.Entry<String, Object> entry : values.valueSet()) {
8125224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            String key = entry.getKey();
8135224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            if (key.startsWith(Downloads.Impl.RequestHeaders.INSERT_KEY_PREFIX)) {
8145224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String headerLine = entry.getValue().toString();
8155224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                if (!headerLine.contains(":")) {
8165224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                    throw new IllegalArgumentException("Invalid HTTP header line: " + headerLine);
8175224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                }
8185224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String[] parts = headerLine.split(":", 2);
8195224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_HEADER, parts[0].trim());
8205224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                rowValues.put(Downloads.Impl.RequestHeaders.COLUMN_VALUE, parts[1].trim());
8215224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.insert(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, null, rowValues);
8225224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
8235224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
8245224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
8255224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
8265224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
8275224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Handle a query for the custom request headers registered for a download.
8285224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
8295224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private Cursor queryRequestHeaders(SQLiteDatabase db, Uri uri) {
8305224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String where = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "="
8315224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                       + getDownloadIdFromUri(uri);
8325224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl.RequestHeaders.COLUMN_HEADER,
8335224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                            Downloads.Impl.RequestHeaders.COLUMN_VALUE};
8345224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        Cursor cursor = db.query(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, projection, where,
8355224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                                 null, null, null, null);
8365224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return new ReadOnlyCursorWrapper(cursor);
8375224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
8385224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
8395224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
8405224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     * Delete request headers for downloads matching the given query.
8415224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
8425224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private void deleteRequestHeaders(SQLiteDatabase db, String where, String[] whereArgs) {
8435224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        String[] projection = new String[] {Downloads.Impl._ID};
844b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        Cursor cursor = db.query(DB_TABLE, projection, where, whereArgs, null, null, null, null);
8455224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        try {
8465224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
8475224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                long id = cursor.getLong(0);
8485224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                String idWhere = Downloads.Impl.RequestHeaders.COLUMN_DOWNLOAD_ID + "=" + id;
8495224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                db.delete(Downloads.Impl.RequestHeaders.HEADERS_DB_TABLE, idWhere, null);
8505224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            }
8515224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        } finally {
8525224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard            cursor.close();
8535224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        }
8545224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
8555224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
8565224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    /**
8573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @return true if we should restrict the columns readable by this caller
8585224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard     */
8595224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    private boolean shouldRestrictVisibility() {
8605224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        int callingUid = Binder.getCallingUid();
8615224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard        return Binder.getCallingPid() != Process.myPid() &&
8625224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mSystemUid &&
8635224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                callingUid != mDefContainerUid &&
8645224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard                Process.supportsProcesses();
8655224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard    }
8665224c6fbf20b4803a580ef449ab87ebfbbfedb78Steve Howard
86757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
86857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Updates a row in the database
86957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
87057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
87157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int update(final Uri uri, final ContentValues values,
87257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String where, final String[] whereArgs) {
8731fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
8741fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(where, sAppReadableColumnsSet);
8751fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
87657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
87757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
87857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
8791fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        boolean startService = false;
8801fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
881e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        if (values.containsKey(Downloads.Impl.COLUMN_DELETED)) {
882e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            if (values.getAsInteger(Downloads.Impl.COLUMN_DELETED) == 1) {
883e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                // some rows are to be 'deleted'. need to start DownloadService.
884e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori                startService = true;
885e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            }
886e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori        }
887e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
8881fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ContentValues filteredValues;
8891fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (Binder.getCallingPid() != Process.myPid()) {
8901fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = new ContentValues();
8917dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);
8927dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues);
8937dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL);
8941fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (i != null) {
8957dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru                filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i);
8961fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                startService = true;
8971fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
898e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori
8997dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);
9007dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues);
901e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, values, filteredValues);
9027dd92fa94df0a13b4592ee636b7aa2b605f6b473Jean-Baptiste Queru            copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues);
903e00c31208405bd2e4c88e069df7a2b15237f70bfVasu Nori            copyInteger(Downloads.Impl.COLUMN_DELETED, values, filteredValues);
9041fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        } else {
9051fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            filteredValues = values;
906a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            String filename = values.getAsString(Downloads.Impl._DATA);
907a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            if (filename != null) {
908a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                Cursor c = query(uri, new String[]
909a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                        { Downloads.Impl.COLUMN_TITLE }, null, null, null);
9103398db8f3b195959faa2a7cf09918f364432ac28Steve Howard                if (!c.moveToFirst() || c.getString(0).isEmpty()) {
9113398db8f3b195959faa2a7cf09918f364432ac28Steve Howard                    values.put(Downloads.Impl.COLUMN_TITLE, new File(filename).getName());
912a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                }
913a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins                c.close();
914a2028ed4141075da8199ba04a499be77734a85aeLeon Scroggins            }
91571e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard
91671e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            Integer status = values.getAsInteger(Downloads.Impl.COLUMN_STATUS);
91771e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            boolean isRestart = status != null && status == Downloads.Impl.STATUS_PENDING;
918d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            boolean isUserBypassingSizeLimit =
919d319729622da1893e895f2e35f41d01ecdca3705Steve Howard                values.containsKey(Downloads.Impl.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT);
920d319729622da1893e895f2e35f41d01ecdca3705Steve Howard            if (isRestart || isUserBypassingSizeLimit) {
92171e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard                startService = true;
92271e7fda9135a0915af1fd419d07ebf85ad09beb4Steve Howard            }
92357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
9243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
92557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
92657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
9273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
9283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
9293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
9303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
931e610c0502c00689411624c00c3f81497df93b202Steve Howard                SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
9321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                if (filteredValues.size() > 0) {
933e610c0502c00689411624c00c3f81497df93b202Steve Howard                    count = db.update(DB_TABLE, filteredValues, selection.getSelection(),
934e610c0502c00689411624c00c3f81497df93b202Steve Howard                            selection.getParameters());
9351fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                } else {
9361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                    count = 0;
9371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                }
93857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
9393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
9413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.d(Constants.TAG, "updating unknown/invalid URI: " + uri);
94257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot update URI: " + uri);
94357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
9443d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
9453d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
9461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (startService) {
9471fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            Context context = getContext();
9481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            context.startService(new Intent(context, DownloadService.class));
9491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
95057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
95157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
95257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
9533d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    /**
9543d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * Notify of a change through both URIs (/my_downloads and /all_downloads)
9553d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uri either URI for the changed download(s)
9563d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     * @param uriMatch the match ID from {@link #sURIMatcher}
9573d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard     */
9583d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void notifyContentChanged(final Uri uri, int uriMatch) {
9593d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Long downloadId = null;
9603d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
9613d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            downloadId = Long.parseLong(getDownloadIdFromUri(uri));
9623d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9633d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        for (Uri uriToNotify : BASE_URIS) {
9643d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (downloadId != null) {
9653d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                uriToNotify = ContentUris.withAppendedId(uriToNotify, downloadId);
9663d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
9673d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            getContext().getContentResolver().notifyChange(uriToNotify, null);
9683d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
9693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
9703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
971e610c0502c00689411624c00c3f81497df93b202Steve Howard    private SqlSelection getWhereClause(final Uri uri, final String where, final String[] whereArgs,
972e610c0502c00689411624c00c3f81497df93b202Steve Howard            int uriMatch) {
973e610c0502c00689411624c00c3f81497df93b202Steve Howard        SqlSelection selection = new SqlSelection();
974e610c0502c00689411624c00c3f81497df93b202Steve Howard        selection.appendClause(where, whereArgs);
9753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (uriMatch == MY_DOWNLOADS_ID || uriMatch == ALL_DOWNLOADS_ID) {
976e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(Downloads.Impl._ID + " = ?", getDownloadIdFromUri(uri));
977b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
978e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root        if ((uriMatch == MY_DOWNLOADS || uriMatch == MY_DOWNLOADS_ID)
979e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root                && getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ALL)
980e1823c84698006aa26a8c5dcfa5c4034858dfbe3Kenny Root                != PackageManager.PERMISSION_GRANTED) {
981e610c0502c00689411624c00c3f81497df93b202Steve Howard            selection.appendClause(
982e610c0502c00689411624c00c3f81497df93b202Steve Howard                    Constants.UID + "= ? OR " + Downloads.Impl.COLUMN_OTHER_UID + "= ?",
983e610c0502c00689411624c00c3f81497df93b202Steve Howard                    Binder.getCallingUid(), Binder.getCallingPid());
984b06b739b078ce4b00600487cfec31659647bf31fSteve Howard        }
985e610c0502c00689411624c00c3f81497df93b202Steve Howard        return selection;
986b06b739b078ce4b00600487cfec31659647bf31fSteve Howard    }
987b06b739b078ce4b00600487cfec31659647bf31fSteve Howard
98857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
98957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Deletes a row in the database
99057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
99157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
99257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    public int delete(final Uri uri, final String where,
99357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            final String[] whereArgs) {
9941fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
9951fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Helpers.validateSelection(where, sAppReadableColumnsSet);
9961fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
99757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
99857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int count;
99957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        int match = sURIMatcher.match(uri);
100057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        switch (match) {
10013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS:
10023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case MY_DOWNLOADS_ID:
10033d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS:
10043d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            case ALL_DOWNLOADS_ID:
1005e610c0502c00689411624c00c3f81497df93b202Steve Howard                SqlSelection selection = getWhereClause(uri, where, whereArgs, match);
1006e610c0502c00689411624c00c3f81497df93b202Steve Howard                deleteRequestHeaders(db, selection.getSelection(), selection.getParameters());
1007e610c0502c00689411624c00c3f81497df93b202Steve Howard                count = db.delete(DB_TABLE, selection.getSelection(), selection.getParameters());
100857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                break;
10093d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
10103d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            default:
10113d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.d(Constants.TAG, "deleting unknown/invalid URI: " + uri);
101257f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project                throw new UnsupportedOperationException("Cannot delete URI: " + uri);
101357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
10143d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        notifyContentChanged(uri, match);
101557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return count;
101657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
101771aab521efba9b28779541440c797220ec98ac97Steve Howard
101857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    /**
101957f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     * Remotely opens a file
102057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project     */
102157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    @Override
10223d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
102357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (Constants.LOGVV) {
10243d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            logVerboseOpenFileInfo(uri, mode);
102557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
10261fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10273d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(uri, new String[] {"_data"}, null, null, null);
10283d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        String path;
10293d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        try {
10303d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            int count = (cursor != null) ? cursor.getCount() : 0;
10313d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (count != 1) {
10323d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                // If there is not exactly one result, throw an appropriate exception.
10333d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (count == 0) {
10343d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    throw new FileNotFoundException("No entry for " + uri);
10353d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
10363d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                throw new FileNotFoundException("Multiple items at " + uri);
10371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
10383d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
10393d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            cursor.moveToFirst();
10403d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            path = cursor.getString(0);
10413d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } finally {
10423d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (cursor != null) {
10433d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                cursor.close();
10441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            }
10451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
10471fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (path == null) {
10481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("No filename found.");
10491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (!Helpers.isFilenameValid(path)) {
10511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("Invalid filename.");
10521fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (!"r".equals(mode)) {
10541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("Bad mode for " + uri + ": " + mode);
10551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
10563d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
10571fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        ParcelFileDescriptor ret = ParcelFileDescriptor.open(new File(path),
10581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                ParcelFileDescriptor.MODE_READ_ONLY);
10591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
106057f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        if (ret == null) {
10611fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            if (Constants.LOGV) {
10621fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project                Log.v(Constants.TAG, "couldn't open file");
106357f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project            }
10641fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new FileNotFoundException("couldn't open file");
106557f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        }
106657f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project        return ret;
106757f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project    }
106857f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project
10693d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    private void logVerboseOpenFileInfo(Uri uri, String mode) {
10703d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Log.v(Constants.TAG, "openFile uri: " + uri + ", mode: " + mode
10713d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                + ", uid: " + Binder.getCallingUid());
10723d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        Cursor cursor = query(Downloads.Impl.CONTENT_URI,
10733d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                new String[] { "_id" }, null, null, "_id");
10743d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
10753d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.v(Constants.TAG, "null cursor in openFile");
10763d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10773d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (!cursor.moveToFirst()) {
10783d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "empty cursor in openFile");
10793d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            } else {
10803d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                do {
10813d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    Log.v(Constants.TAG, "row " + cursor.getInt(0) + " available");
10823d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                } while(cursor.moveToNext());
10833d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10843d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            cursor.close();
10853d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
10863d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        cursor = query(uri, new String[] { "_data" }, null, null, null);
10873d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        if (cursor == null) {
10883d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            Log.v(Constants.TAG, "null cursor in openFile");
10893d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        } else {
10903d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            if (!cursor.moveToFirst()) {
10913d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "empty cursor in openFile");
10923d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            } else {
10933d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                String filename = cursor.getString(0);
10943d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                Log.v(Constants.TAG, "filename in openFile: " + filename);
10953d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                if (new java.io.File(filename).isFile()) {
10963d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                    Log.v(Constants.TAG, "file exists in openFile");
10973d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard                }
10983d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard            }
10993d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard           cursor.close();
11003d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard        }
11013d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard    }
11023d55d829c03fe78ad8cdab119293efb6c6e49c64Steve Howard
11031fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyInteger(String key, ContentValues from, ContentValues to) {
11041fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Integer i = from.getAsInteger(key);
11051fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (i != null) {
11061fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, i);
11071fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11081fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
11091fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11101fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyBoolean(String key, ContentValues from, ContentValues to) {
11111fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        Boolean b = from.getAsBoolean(key);
11121fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (b != null) {
11131fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, b);
11141fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11151fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
11161fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11171fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private static final void copyString(String key, ContentValues from, ContentValues to) {
11181fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        String s = from.getAsString(key);
11191fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        if (s != null) {
11201fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            to.put(key, s);
11211fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11221fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
11231fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
1124a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    private static final void copyStringWithDefault(String key, ContentValues from,
1125a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            ContentValues to, String defaultValue) {
1126a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        copyString(key, from, to);
1127a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        if (!to.containsKey(key)) {
1128a89321ea04ced76d06f60f5909be203cb654a830Steve Howard            to.put(key, defaultValue);
1129a89321ea04ced76d06f60f5909be203cb654a830Steve Howard        }
1130a89321ea04ced76d06f60f5909be203cb654a830Steve Howard    }
1131a89321ea04ced76d06f60f5909be203cb654a830Steve Howard
11321fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    private class ReadOnlyCursorWrapper extends CursorWrapper implements CrossProcessCursor {
11331fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public ReadOnlyCursorWrapper(Cursor cursor) {
11341fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            super(cursor);
11351fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            mCursor = (CrossProcessCursor) cursor;
11361fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11371fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11381fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean deleteRow() {
11391fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new SecurityException("Download manager cursors are read-only");
11401fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11411fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11421fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean commitUpdates() {
11431fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            throw new SecurityException("Download manager cursors are read-only");
11441fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11451fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11461fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public void fillWindow(int pos, CursorWindow window) {
11471fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            mCursor.fillWindow(pos, window);
11481fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11491fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11501fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public CursorWindow getWindow() {
11511fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            return mCursor.getWindow();
11521fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11531fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11541fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        public boolean onMove(int oldPosition, int newPosition) {
11551fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project            return mCursor.onMove(oldPosition, newPosition);
11561fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        }
11571fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
11581fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project        private CrossProcessCursor mCursor;
11591fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project    }
11601fbad9cfa532f13f6cf03f542febf2e4689edec5The Android Open Source Project
116157f55b3cb4f7e4136cde8d1ea12c1e70ec90336The Android Open Source Project}
1162