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_MEDIA_TYPE; 21 import static android.app.DownloadManager.COLUMN_URI; 22 import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; 23 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.content.pm.PackageInstaller; 32 import android.content.pm.PackageManager; 33 import android.database.Cursor; 34 import android.net.Uri; 35 import android.os.Process; 36 import android.provider.DocumentsContract; 37 import android.provider.Downloads.Impl.RequestHeaders; 38 import android.provider.MediaStore; 39 import android.util.Log; 40 41 import java.io.File; 42 43 public class OpenHelper { 44 /** 45 * Build and start an {@link Intent} to view the download with given ID, 46 * handling subtleties around installing packages. 47 */ startViewIntent(Context context, long id, int intentFlags)48 public static boolean startViewIntent(Context context, long id, int intentFlags) { 49 final Intent intent = OpenHelper.buildViewIntent(context, id); 50 if (intent == null) { 51 Log.w(TAG, "No intent built for " + id); 52 return false; 53 } 54 55 intent.addFlags(intentFlags); 56 return startViewIntent(context, intent); 57 } 58 startViewIntent(Context context, Intent intent)59 public static boolean startViewIntent(Context context, Intent intent) { 60 try { 61 context.startActivity(intent); 62 return true; 63 } catch (ActivityNotFoundException e) { 64 Log.w(TAG, "Failed to start " + intent + ": " + e); 65 return false; 66 } 67 } 68 buildViewIntentForMediaStoreDownload(Context context, Uri documentUri)69 public static Intent buildViewIntentForMediaStoreDownload(Context context, 70 Uri documentUri) { 71 final long mediaStoreId = MediaStoreDownloadsHelper.getMediaStoreId( 72 DocumentsContract.getDocumentId(documentUri)); 73 final Uri queryUri = ContentUris.withAppendedId( 74 MediaStore.Downloads.EXTERNAL_CONTENT_URI, mediaStoreId); 75 try (Cursor cursor = context.getContentResolver().query( 76 queryUri, null, null, null)) { 77 if (cursor.moveToFirst()) { 78 final String mimeType = cursor.getString( 79 cursor.getColumnIndex(MediaStore.Downloads.MIME_TYPE)); 80 81 final Intent intent = new Intent(Intent.ACTION_VIEW); 82 intent.setDataAndType(documentUri, mimeType); 83 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 84 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 85 86 if ("application/vnd.android.package-archive".equals(mimeType)) { 87 // Also splice in details about where it came from 88 intent.putExtra(Intent.EXTRA_ORIGINATING_URI, 89 getCursorUri(cursor, MediaStore.Downloads.DOWNLOAD_URI)); 90 intent.putExtra(Intent.EXTRA_REFERRER, 91 getCursorUri(cursor, MediaStore.Downloads.REFERER_URI)); 92 final String ownerPackageName = getCursorString(cursor, 93 MediaStore.Downloads.OWNER_PACKAGE_NAME); 94 final int ownerUid = getPackageUid(context, ownerPackageName); 95 if (ownerUid > 0) { 96 intent.putExtra(Intent.EXTRA_ORIGINATING_UID, ownerUid); 97 } 98 } 99 return intent; 100 } 101 } 102 return null; 103 } 104 105 /** 106 * Build an {@link Intent} to view the download with given ID, handling 107 * subtleties around installing packages. 108 */ buildViewIntent(Context context, long id)109 private static Intent buildViewIntent(Context context, long id) { 110 final DownloadManager downManager = (DownloadManager) context.getSystemService( 111 Context.DOWNLOAD_SERVICE); 112 downManager.setAccessAllDownloads(true); 113 downManager.setAccessFilename(true); 114 115 final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id)); 116 try { 117 if (!cursor.moveToFirst()) { 118 return null; 119 } 120 121 final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME); 122 String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE); 123 mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType); 124 125 final Uri documentUri = DocumentsContract.buildDocumentUri( 126 Constants.STORAGE_AUTHORITY, String.valueOf(id)); 127 128 final Intent intent = new Intent(Intent.ACTION_VIEW); 129 intent.setDataAndType(documentUri, mimeType); 130 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 131 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 132 133 if ("application/vnd.android.package-archive".equals(mimeType)) { 134 // Also splice in details about where it came from 135 final Uri remoteUri = getCursorUri(cursor, COLUMN_URI); 136 intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri); 137 intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id)); 138 intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id)); 139 } 140 141 return intent; 142 } finally { 143 cursor.close(); 144 } 145 } 146 getRefererUri(Context context, long id)147 private static Uri getRefererUri(Context context, long id) { 148 final Uri headersUri = Uri.withAppendedPath( 149 ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id), 150 RequestHeaders.URI_SEGMENT); 151 final Cursor headers = context.getContentResolver() 152 .query(headersUri, null, null, null, null); 153 try { 154 while (headers.moveToNext()) { 155 final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER); 156 if ("Referer".equalsIgnoreCase(header)) { 157 return getCursorUri(headers, RequestHeaders.COLUMN_VALUE); 158 } 159 } 160 } finally { 161 headers.close(); 162 } 163 return null; 164 } 165 getOriginatingUid(Context context, long id)166 private static int getOriginatingUid(Context context, long id) { 167 final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id); 168 final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID}, 169 null, null, null); 170 if (cursor != null) { 171 try { 172 if (cursor.moveToFirst()) { 173 final int uid = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID)); 174 if (uid != Process.myUid()) { 175 return uid; 176 } 177 } 178 } finally { 179 cursor.close(); 180 } 181 } 182 return PackageInstaller.SessionParams.UID_UNKNOWN; 183 } 184 getPackageUid(Context context, String packageName)185 private static int getPackageUid(Context context, String packageName) { 186 if (packageName == null) { 187 return -1; 188 } 189 try { 190 return context.getPackageManager().getPackageUid(packageName, 0); 191 } catch (PackageManager.NameNotFoundException e) { 192 Log.e(TAG, "Couldn't get uid for " + packageName, e); 193 return -1; 194 } 195 } 196 getCursorString(Cursor cursor, String column)197 private static String getCursorString(Cursor cursor, String column) { 198 return cursor.getString(cursor.getColumnIndexOrThrow(column)); 199 } 200 getCursorUri(Cursor cursor, String column)201 private static Uri getCursorUri(Cursor cursor, String column) { 202 final String uriString = cursor.getString(cursor.getColumnIndexOrThrow(column)); 203 return uriString == null ? null : Uri.parse(uriString); 204 } 205 getCursorFile(Cursor cursor, String column)206 private static File getCursorFile(Cursor cursor, String column) { 207 return new File(cursor.getString(cursor.getColumnIndexOrThrow(column))); 208 } 209 } 210