diff options
Diffstat (limited to 'java/src')
5 files changed, 75 insertions, 27 deletions
diff --git a/java/src/com/android/intentresolver/ChooserRequestParameters.java b/java/src/com/android/intentresolver/ChooserRequestParameters.java index 039f50e9..b3f5a722 100644 --- a/java/src/com/android/intentresolver/ChooserRequestParameters.java +++ b/java/src/com/android/intentresolver/ChooserRequestParameters.java @@ -33,6 +33,7 @@ import android.util.Log;  import android.util.Pair;  import com.android.intentresolver.flags.FeatureFlagRepository; +import com.android.intentresolver.util.UriFilters;  import com.google.common.collect.ImmutableList; @@ -320,7 +321,8 @@ public class ChooserRequestParameters {                  ChooserAction.class,                  true,                  true) -            .collect(toImmutableList()); +                .filter(UriFilters::hasValidIcon) +                .collect(toImmutableList());      }      @Nullable diff --git a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java index 69d8c49f..b61f01b3 100644 --- a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java +++ b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java @@ -19,6 +19,7 @@ package com.android.intentresolver.contentpreview;  import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;  import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE; +import static com.android.intentresolver.util.UriFilters.isOwnedByCurrentUser;  import android.content.ClipData;  import android.content.ClipDescription; @@ -306,14 +307,14 @@ public final class ChooserContentPreviewUi {          List<Uri> uris = new ArrayList<>();          if (Intent.ACTION_SEND.equals(targetIntent.getAction())) {              Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); -            if (ContentPreviewUi.validForContentPreview(uri)) { +            if (isOwnedByCurrentUser(uri)) {                  uris.add(uri);              }          } else {              List<Uri> receivedUris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);              if (receivedUris != null) {                  for (Uri uri : receivedUris) { -                    if (ContentPreviewUi.validForContentPreview(uri)) { +                    if (isOwnedByCurrentUser(uri)) {                          uris.add(uri);                      }                  } diff --git a/java/src/com/android/intentresolver/contentpreview/ContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/ContentPreviewUi.java index 4e343a17..c0859e53 100644 --- a/java/src/com/android/intentresolver/contentpreview/ContentPreviewUi.java +++ b/java/src/com/android/intentresolver/contentpreview/ContentPreviewUi.java @@ -16,16 +16,11 @@  package com.android.intentresolver.contentpreview; -import static android.content.ContentProvider.getUserIdFromUri; -  import android.animation.ObjectAnimator;  import android.animation.ValueAnimator;  import android.content.res.Resources;  import android.graphics.Bitmap; -import android.net.Uri; -import android.os.UserHandle;  import android.text.TextUtils; -import android.util.Log;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; @@ -63,24 +58,6 @@ abstract class ContentPreviewUi {          return actions;      } -    /** -     * Indicate if the incoming content URI should be allowed. -     * -     * @param uri the uri to test -     * @return true if the URI is allowed for content preview -     */ -    protected static boolean validForContentPreview(Uri uri) throws SecurityException { -        if (uri == null) { -            return false; -        } -        int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); -        if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { -            Log.e(ContentPreviewUi.TAG, "dropped invalid content URI belonging to user " + userId); -            return false; -        } -        return true; -    } -      protected static void updateViewWithImage(ImageView imageView, Bitmap image) {          if (image == null) {              imageView.setVisibility(View.GONE); diff --git a/java/src/com/android/intentresolver/contentpreview/TextContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/TextContentPreviewUi.java index 3f662ce3..dc7a68b1 100644 --- a/java/src/com/android/intentresolver/contentpreview/TextContentPreviewUi.java +++ b/java/src/com/android/intentresolver/contentpreview/TextContentPreviewUi.java @@ -16,6 +16,8 @@  package com.android.intentresolver.contentpreview; +import static com.android.intentresolver.util.UriFilters.isOwnedByCurrentUser; +  import android.content.res.Resources;  import android.net.Uri;  import android.text.TextUtils; @@ -108,7 +110,7 @@ class TextContentPreviewUi extends ContentPreviewUi {          ImageView previewThumbnailView = contentPreviewLayout.findViewById(                  com.android.internal.R.id.content_preview_thumbnail); -        if (!validForContentPreview(mPreviewThumbnail) || minimalPreview) { +        if (!isOwnedByCurrentUser(mPreviewThumbnail) || minimalPreview) {              previewThumbnailView.setVisibility(View.GONE);          } else {              mImageLoader.loadImage( diff --git a/java/src/com/android/intentresolver/util/UriFilters.kt b/java/src/com/android/intentresolver/util/UriFilters.kt new file mode 100644 index 00000000..8714c314 --- /dev/null +++ b/java/src/com/android/intentresolver/util/UriFilters.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("UriFilters") + +package com.android.intentresolver.util + +import android.content.ContentProvider.getUserIdFromUri +import android.content.ContentResolver.SCHEME_CONTENT +import android.graphics.drawable.Icon.TYPE_URI +import android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP +import android.net.Uri +import android.os.UserHandle +import android.service.chooser.ChooserAction + +/** + * Checks if the [Uri] is a `content://` uri which references the current user (from process uid). + * + * MediaStore interprets the user field of a content:// URI as a UserId and applies it if the caller + * holds INTERACT_ACROSS_USERS permission. (Example: `content://10@media/images/1234`) + * + * No URI content should be loaded unless it passes this check since the caller would not have + * permission to read it. + * + * @return false if this is a content:// [Uri] which references another user + */ +val Uri?.ownedByCurrentUser: Boolean +    @JvmName("isOwnedByCurrentUser") +    get() = this?.let { +        when (getUserIdFromUri(this, UserHandle.USER_CURRENT)) { +            UserHandle.USER_CURRENT, +            UserHandle.myUserId() -> true +            else -> false +        } +    } == true + +/** Does the [Uri] reference a content provider ('content://')? */ +internal val Uri.contentScheme: Boolean +    get() = scheme == SCHEME_CONTENT + +/** + * Checks if the Icon of a [ChooserAction] backed by content:// [Uri] is safe for display. + * + * @param action the chooser action + * @see [Uri.ownedByCurrentUser] + */ +fun hasValidIcon(action: ChooserAction) = +    with(action.icon) { +        when (type) { +            TYPE_URI, +            TYPE_URI_ADAPTIVE_BITMAP -> !uri.contentScheme || uri.ownedByCurrentUser +            else -> true +        } +    }  |