1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.downloads; 18 19 import static android.app.DownloadManager.COLUMN_LOCAL_FILENAME; 20 import static android.app.DownloadManager.COLUMN_LOCAL_URI; 21 import static android.app.DownloadManager.COLUMN_MEDIA_TYPE; 22 import static android.app.DownloadManager.COLUMN_URI; 23 import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; 24 import static com.android.providers.downloads.Constants.TAG; 25 26 import android.app.DownloadManager; 27 import android.content.ActivityNotFoundException; 28 import android.content.ContentUris; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.database.Cursor; 32 import android.net.Uri; 33 import android.os.StrictMode; 34 import android.provider.DocumentsContract; 35 import android.provider.Downloads.Impl.RequestHeaders; 36 import android.util.Log; 37 38 import java.io.File; 39 40 public class OpenHelper { 41 /** 42 * Build and start an {@link Intent} to view the download with given ID, 43 * handling subtleties around installing packages. 44 */ startViewIntent(Context context, long id, int intentFlags)45 public static boolean startViewIntent(Context context, long id, int intentFlags) { 46 final Intent intent = OpenHelper.buildViewIntent(context, id); 47 if (intent == null) { 48 Log.w(TAG, "No intent built for " + id); 49 return false; 50 } 51 52 intent.addFlags(intentFlags); 53 try { 54 StrictMode.disableDeathOnFileUriExposure(); 55 context.startActivity(intent); 56 return true; 57 } catch (ActivityNotFoundException e) { 58 Log.w(TAG, "Failed to start " + intent + ": " + e); 59 return false; 60 } finally { 61 StrictMode.enableDeathOnFileUriExposure(); 62 } 63 } 64 65 /** 66 * Build an {@link Intent} to view the download with given ID, handling 67 * subtleties around installing packages. 68 */ buildViewIntent(Context context, long id)69 private static Intent buildViewIntent(Context context, long id) { 70 final DownloadManager downManager = (DownloadManager) context.getSystemService( 71 Context.DOWNLOAD_SERVICE); 72 downManager.setAccessAllDownloads(true); 73 downManager.setAccessFilename(true); 74 75 final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id)); 76 try { 77 if (!cursor.moveToFirst()) { 78 return null; 79 } 80 81 final Uri localUri = getCursorUri(cursor, COLUMN_LOCAL_URI); 82 final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME); 83 String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); 84 mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType); 85 86 final Uri documentUri = DocumentsContract.buildDocumentUri( 87 Constants.STORAGE_AUTHORITY, String.valueOf(id)); 88 89 final Intent intent = new Intent(Intent.ACTION_VIEW); 90 91 if ("application/vnd.android.package-archive".equals(mimeType)) { 92 // PackageInstaller doesn't like content URIs, so open file 93 intent.setDataAndType(localUri, mimeType); 94 95 // Also splice in details about where it came from 96 final Uri remoteUri = getCursorUri(cursor, COLUMN_URI); 97 intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri); 98 intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id)); 99 intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id)); 100 } else { 101 intent.setDataAndType(documentUri, mimeType); 102 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 103 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 104 } 105 106 return intent; 107 } finally { 108 cursor.close(); 109 } 110 } 111 getRefererUri(Context context, long id)112 private static Uri getRefererUri(Context context, long id) { 113 final Uri headersUri = Uri.withAppendedPath( 114 ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), 115 RequestHeaders.URI_SEGMENT); 116 final Cursor headers = context.getContentResolver() 117 .query(headersUri, null, null, null, null); 118 try { 119 while (headers.moveToNext()) { 120 final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER); 121 if ("Referer".equalsIgnoreCase(header)) { 122 return getCursorUri(headers, RequestHeaders.COLUMN_VALUE); 123 } 124 } 125 } finally { 126 headers.close(); 127 } 128 return null; 129 } 130 getOriginatingUid(Context context, long id)131 private static int getOriginatingUid(Context context, long id) { 132 final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id); 133 final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID}, 134 null, null, null); 135 if (cursor != null) { 136 try { 137 if (cursor.moveToFirst()) { 138 return cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID)); 139 } 140 } finally { 141 cursor.close(); 142 } 143 } 144 return -1; 145 } 146 getCursorString(Cursor cursor, String column)147 private static String getCursorString(Cursor cursor, String column) { 148 return cursor.getString(cursor.getColumnIndexOrThrow(column)); 149 } 150 getCursorUri(Cursor cursor, String column)151 private static Uri getCursorUri(Cursor cursor, String column) { 152 return Uri.parse(getCursorString(cursor, column)); 153 } 154 getCursorFile(Cursor cursor, String column)155 private static File getCursorFile(Cursor cursor, String column) { 156 return new File(cursor.getString(cursor.getColumnIndexOrThrow(column))); 157 } 158 } 159