1c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey/* 2c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project 3c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * 4c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * you may not use this file except in compliance with the License. 6c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * You may obtain a copy of the License at 7c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * 8c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * 10c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * See the License for the specific language governing permissions and 14c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * limitations under the License. 15c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey */ 16c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 17c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeypackage com.android.providers.downloads; 18c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 19169ab531ca6bb2f43ed18fe2b03f66c808f12b81Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_LOCAL_FILENAME; 20c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_MEDIA_TYPE; 21c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_URI; 22c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; 233b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey 248ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport static com.android.providers.downloads.Constants.TAG; 25c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 26c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.app.DownloadManager; 278ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport android.content.ActivityNotFoundException; 28c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.ContentUris; 29c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.Context; 30c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.Intent; 310a1a4287841fa10274c610de31e08765ffa0c269Ben Linimport android.content.pm.PackageInstaller; 32c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.database.Cursor; 33c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.net.Uri; 34b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shuklaimport android.os.Process; 353b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewskiimport android.provider.DocumentsContract; 36c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.provider.Downloads.Impl.RequestHeaders; 378ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport android.util.Log; 38c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 395cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkeyimport java.io.File; 405cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey 41c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeypublic class OpenHelper { 42c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey /** 438ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey * Build and start an {@link Intent} to view the download with given ID, 448ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey * handling subtleties around installing packages. 45c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey */ 468ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey public static boolean startViewIntent(Context context, long id, int intentFlags) { 478ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey final Intent intent = OpenHelper.buildViewIntent(context, id); 488ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey if (intent == null) { 498ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey Log.w(TAG, "No intent built for " + id); 508ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey return false; 518ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey } 528ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey 538ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey intent.addFlags(intentFlags); 548ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey try { 558ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey context.startActivity(intent); 568ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey return true; 578ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey } catch (ActivityNotFoundException e) { 588ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey Log.w(TAG, "Failed to start " + intent + ": " + e); 598ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey return false; 608ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey } 618ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey } 628ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey 638ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey /** 648ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey * Build an {@link Intent} to view the download with given ID, handling 658ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey * subtleties around installing packages. 668ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey */ 678ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey private static Intent buildViewIntent(Context context, long id) { 68c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final DownloadManager downManager = (DownloadManager) context.getSystemService( 69c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey Context.DOWNLOAD_SERVICE); 70c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey downManager.setAccessAllDownloads(true); 71307b464de52444531365a55eb700da6ba2b28ad2Jeff Sharkey downManager.setAccessFilename(true); 72c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 73c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id)); 74c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey try { 75c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey if (!cursor.moveToFirst()) { 768ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey return null; 77c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 78c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 795cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME); 80169ab531ca6bb2f43ed18fe2b03f66c808f12b81Jeff Sharkey String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); 815cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType); 82c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 833b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski final Uri documentUri = DocumentsContract.buildDocumentUri( 843b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski Constants.STORAGE_AUTHORITY, String.valueOf(id)); 853b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski 86c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final Intent intent = new Intent(Intent.ACTION_VIEW); 873b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey intent.setDataAndType(documentUri, mimeType); 883b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 893b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 90c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 91c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey if ("application/vnd.android.package-archive".equals(mimeType)) { 92c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey // Also splice in details about where it came from 93c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final Uri remoteUri = getCursorUri(cursor, COLUMN_URI); 94c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri); 95c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id)); 9646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id)); 97c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 98c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 99c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey return intent; 100c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } finally { 101c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey cursor.close(); 102c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 103c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 104c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 105c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey private static Uri getRefererUri(Context context, long id) { 106c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final Uri headersUri = Uri.withAppendedPath( 107c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), 108c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey RequestHeaders.URI_SEGMENT); 109c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final Cursor headers = context.getContentResolver() 110c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey .query(headersUri, null, null, null, null); 111c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey try { 112c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey while (headers.moveToNext()) { 113c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER); 114c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey if ("Referer".equalsIgnoreCase(header)) { 115c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey return getCursorUri(headers, RequestHeaders.COLUMN_VALUE); 116c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 117c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 118c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } finally { 119c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey headers.close(); 120c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 121c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey return null; 122c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 123c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 12446bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver private static int getOriginatingUid(Context context, long id) { 12546bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id); 12646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID}, 12746bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver null, null, null); 12846bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver if (cursor != null) { 12946bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver try { 13046bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver if (cursor.moveToFirst()) { 131b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla final int uid = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID)); 132b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla if (uid != Process.myUid()) { 133b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla return uid; 134b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla } 13546bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver } 13646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver } finally { 13746bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver cursor.close(); 13846bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver } 13946bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver } 1400a1a4287841fa10274c610de31e08765ffa0c269Ben Lin return PackageInstaller.SessionParams.UID_UNKNOWN; 14146bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver } 14246bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver 143c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey private static String getCursorString(Cursor cursor, String column) { 144c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey return cursor.getString(cursor.getColumnIndexOrThrow(column)); 145c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 146c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 147c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey private static Uri getCursorUri(Cursor cursor, String column) { 148c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey return Uri.parse(getCursorString(cursor, column)); 149c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey } 150c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey 1515cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey private static File getCursorFile(Cursor cursor, String column) { 1525cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey return new File(cursor.getString(cursor.getColumnIndexOrThrow(column))); 1535cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey } 154c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey} 155