1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.media; 6 7 import android.app.Activity; 8 import android.content.ContentResolver; 9 import android.content.Intent; 10 import android.net.Uri; 11 import android.os.Bundle; 12 import android.webkit.MimeTypeMap; 13 14 import androidx.annotation.IntDef; 15 16 import org.chromium.base.IntentUtils; 17 import org.chromium.base.Log; 18 import org.chromium.base.metrics.RecordHistogram; 19 import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; 20 21 import java.lang.annotation.Retention; 22 import java.lang.annotation.RetentionPolicy; 23 import java.util.Locale; 24 25 /** 26 * The MediaLauncherActivity handles media-viewing Intents from other apps. It takes the given 27 * content:// URI from the Intent and properly routes it to a media-viewing CustomTabActivity. 28 */ 29 public class MediaLauncherActivity extends Activity { 30 private static final String TAG = "MediaLauncher"; 31 32 // UMA histogram values for media types the user can open. 33 // Keep in sync with MediaLauncherActivityMediaType enum in enums.xml. 34 @IntDef({MediaType.AUDIO, MediaType.IMAGE, MediaType.VIDEO}) 35 @Retention(RetentionPolicy.SOURCE) 36 @interface MediaType { 37 int AUDIO = 0; 38 int IMAGE = 1; 39 int VIDEO = 2; 40 int UNKNOWN = 3; 41 int NUM_ENTRIES = 4; 42 } 43 44 @Override onCreate(Bundle savedInstanceState)45 public void onCreate(Bundle savedInstanceState) { 46 super.onCreate(savedInstanceState); 47 48 Intent input = IntentUtils.sanitizeIntent(getIntent()); 49 Uri contentUri = input.getData(); 50 String mimeType = getMIMEType(contentUri); 51 int mediaType = MediaViewerUtils.getMediaTypeFromMIMEType(mimeType); 52 53 RecordHistogram.recordEnumeratedHistogram( 54 "MediaLauncherActivity.MediaType", mediaType, MediaType.NUM_ENTRIES); 55 56 if (mediaType == MediaType.UNKNOWN) { 57 // With our intent-filter, we should only receive implicit intents with media MIME 58 // types. If we receive a non-media MIME type, it is likely a malicious explicit intent, 59 // so we should not proceed. 60 finish(); 61 return; 62 } 63 64 // TODO(https://crbug.com/800880): Determine file:// URI when possible. 65 Intent intent = MediaViewerUtils.getMediaViewerIntent( 66 contentUri, contentUri, mimeType, false /* allowExternalAppHandlers */); 67 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 68 intent.putExtra(CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, 69 CustomTabIntentDataProvider.LaunchSourceType.MEDIA_LAUNCHER_ACTIVITY); 70 71 boolean success = false; 72 try { 73 startActivity(intent); 74 success = true; 75 } catch (SecurityException e) { 76 Log.w(TAG, "Cannot open content URI: " + contentUri.toString(), e); 77 } 78 79 RecordHistogram.recordBooleanHistogram("MediaLauncherActivity.LaunchResult", success); 80 81 finish(); 82 } 83 getMIMEType(Uri uri)84 private String getMIMEType(Uri uri) { 85 // With a content URI, we can just query the ContentResolver. 86 if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { 87 return getContentResolver().getType(uri); 88 } 89 90 // Otherwise, use the file extension. 91 String filteredUri = filterURI(uri); 92 String fileExtension = MimeTypeMap.getFileExtensionFromUrl(filteredUri); 93 return MimeTypeMap.getSingleton().getMimeTypeFromExtension( 94 fileExtension.toLowerCase(Locale.ROOT)); 95 } 96 97 // MimeTypeMap.getFileExtensionFromUrl fails when the file name includes certain special 98 // characters, so we filter those out of the URI when determining the MIME type. filterURI(Uri uri)99 protected static String filterURI(Uri uri) { 100 String uriString = uri.toString(); 101 int filterIndex = uriString.length(); 102 103 int fragmentIndex = uriString.lastIndexOf('#', filterIndex); 104 if (fragmentIndex >= 0) filterIndex = fragmentIndex; 105 106 int queryIndex = uriString.lastIndexOf('?', filterIndex); 107 if (queryIndex >= 0) filterIndex = queryIndex; 108 109 int extensionIndex = uriString.lastIndexOf('.', filterIndex); 110 if (extensionIndex >= 0) filterIndex = extensionIndex; 111 112 return uriString.substring(0, filterIndex).replaceAll("['$!]", "") 113 + uriString.substring(filterIndex); 114 } 115 } 116