Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8e790a8 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

Visit URIs in intents associated with notifications.

Flag: ACONFIG android.app.VISIT_RISKY_URIS DEVELOPMENT
Bug: 281044385
Test: atest NotificationVisitUrisTest NotificationManagerServiceTest

Change-Id: I692fd020b39d74aa7401e9f2be2c6f6608ce43bc
parent 6f601837
Loading
Loading
Loading
Loading
+77 −8
Original line number Diff line number Diff line
@@ -2205,6 +2205,9 @@ public class Notification implements Parcelable
        private void visitUris(@NonNull Consumer<Uri> visitor) {
            visitIconUri(visitor, getIcon());
            if (actionIntent != null) {
                actionIntent.visitUris(visitor);
            }
        }
        @Override
@@ -2898,6 +2901,21 @@ public class Notification implements Parcelable
            }
        }
        // allPendingIntents should contain all associated intents after parcelling, but it may also
        // contain intents added by the app to extras for their own purposes. We only care about
        // checking the intents known and used by system_server, to avoid the confused deputy issue.
        List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent,
                fullScreenIntent);
        for (PendingIntent intent : pendingIntents) {
            if (intent != null) {
                intent.visitUris(visitor);
            }
        }
        if (mBubbleMetadata != null) {
            mBubbleMetadata.visitUris(visitor);
        }
        if (extras != null) {
            visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class));
            visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class));
@@ -2969,16 +2987,29 @@ public class Notification implements Parcelable
                callPerson.visitUris(visitor);
            }
            visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class));
        }
        if (mBubbleMetadata != null) {
            visitIconUri(visitor, mBubbleMetadata.getIcon());
            // Extras for MediaStyle.
            PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT,
                    PendingIntent.class);
            if (deviceIntent != null) {
                deviceIntent.visitUris(visitor);
            }
        if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
            if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
                WearableExtender extender = new WearableExtender(this);
                extender.visitUris(visitor);
            }
            if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) {
                TvExtender extender = new TvExtender(this);
                extender.visitUris(visitor);
            }
            if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) {
                CarExtender extender = new CarExtender(this);
                extender.visitUris(visitor);
            }
        }
    }
    /**
@@ -10558,6 +10589,16 @@ public class Notification implements Parcelable
            }
        }
        private void visitUris(@NonNull Consumer<Uri> visitor) {
            visitIconUri(visitor, getIcon());
            if (mPendingIntent != null) {
                mPendingIntent.visitUris(visitor);
            }
            if (mDeleteIntent != null) {
                mDeleteIntent.visitUris(visitor);
            }
        }
        /**
         * Builder to construct a {@link BubbleMetadata} object.
         */
@@ -11756,6 +11797,9 @@ public class Notification implements Parcelable
        }
        private void visitUris(@NonNull Consumer<Uri> visitor) {
            if (mDisplayIntent != null) {
                mDisplayIntent.visitUris(visitor);
            }
            for (Action action : mActions) {
                action.visitUris(visitor);
            }
@@ -11908,12 +11952,19 @@ public class Notification implements Parcelable
        /**
         * Returns the unread conversation conveyed by this notification.
         *
         * @see #setUnreadConversation(UnreadConversation)
         */
        public UnreadConversation getUnreadConversation() {
            return mUnreadConversation;
        }
        private void visitUris(@NonNull Consumer<Uri> visitor) {
            if (mUnreadConversation != null) {
                mUnreadConversation.visitUris(visitor);
            }
        }
        /**
         * A class which holds the unread messages from a conversation.
         */
@@ -12065,7 +12116,16 @@ public class Notification implements Parcelable
                        onRead,
                        participants, b.getLong(KEY_TIMESTAMP));
            }
        };
            private void visitUris(@NonNull Consumer<Uri> visitor) {
                if (mReadPendingIntent != null) {
                    mReadPendingIntent.visitUris(visitor);
                }
                if (mReplyPendingIntent != null) {
                    mReplyPendingIntent.visitUris(visitor);
                }
            }
        }
        /**
         * Builder class for {@link CarExtender.UnreadConversation} objects.
@@ -12388,6 +12448,15 @@ public class Notification implements Parcelable
        public boolean isSuppressShowOverApps() {
            return mSuppressShowOverApps;
        }
        private void visitUris(@NonNull Consumer<Uri> visitor) {
            if (mContentIntent != null) {
                mContentIntent.visitUris(visitor);
            }
            if (mDeleteIntent != null) {
                mDeleteIntent.visitUris(visitor);
            }
        }
    }
    /**
+18 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import android.content.IntentSender;
import android.content.pm.PackageManager.ResolveInfoFlagsBits;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -69,6 +71,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * A description of an Intent and target action to perform with it.  Instances
@@ -1460,6 +1463,21 @@ public final class PendingIntent implements Parcelable {
        return sb.toString();
    }

    /**
     * See {@link Intent#visitUris(Consumer)}.
     *
     * @hide
     */
    public void visitUris(@NonNull Consumer<Uri> visitor) {
        if (android.app.Flags.visitRiskyUris()) {
            Intent intent = Binder.withCleanCallingIdentity(this::getIntent);

            if (intent != null) {
                intent.visitUris(visitor);
            }
        }
    }

    /** @hide */
    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
+22 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
/**
 * An intent is an abstract description of an operation to be performed.  It
@@ -8147,6 +8148,27 @@ public class Intent implements Parcelable, Cloneable {
        }
    }
    /**
     * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
     * grants will need to be issued to ensure the recipient of this object is able to render its
     * contents.
     * See b/281044385 for more context and examples about what happens when this isn't done
     * correctly.
     *
     * @hide
     */
    public void visitUris(@NonNull Consumer<Uri> visitor) {
        if (android.app.Flags.visitRiskyUris()) {
            visitor.accept(mData);
            if (mSelector != null) {
                mSelector.visitUris(visitor);
            }
            if (mOriginalIntent != null) {
                mOriginalIntent.visitUris(visitor);
            }
        }
    }
    public static Intent getIntentOld(String uri) throws URISyntaxException {
        Intent intent = getIntentOld(uri, 0);
        intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
+31 −5
Original line number Diff line number Diff line
@@ -1015,6 +1015,11 @@ public class RemoteViews implements Parcelable, Filter {
        public int getActionTag() {
            return SET_PENDING_INTENT_TEMPLATE_TAG;
        }

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            mPendingIntentTemplate.visitUris(visitor);
        }
    }

    /**
@@ -1429,9 +1434,7 @@ public class RemoteViews implements Parcelable, Filter {

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            // TODO(b/281044385): Maybe visit intent URIs. This may require adding a dedicated
            //  visitUris method in the Intent class, since it can contain other intents. Otherwise,
            //  the basic thing to do here would be just visitor.accept(intent.getData()).
            mIntent.visitUris(visitor);
        }
    }

@@ -1511,7 +1514,7 @@ public class RemoteViews implements Parcelable, Filter {

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
            mResponse.visitUris(visitor);
        }
    }

@@ -1560,6 +1563,11 @@ public class RemoteViews implements Parcelable, Filter {
        public int getActionTag() {
            return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
        }

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            mPendingIntent.visitUris(visitor);
        }
    }

    /**
@@ -1633,7 +1641,7 @@ public class RemoteViews implements Parcelable, Filter {

        @Override
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse.
            mResponse.visitUris(visitor);
        }
    }

@@ -2194,6 +2202,10 @@ public class RemoteViews implements Parcelable, Filter {
                    final Icon icon = (Icon) getParameterValue(null);
                    if (icon != null) visitIconUri(icon, visitor);
                    break;
                case INTENT:
                    final Intent intent = (Intent) getParameterValue(null);
                    if (intent != null) intent.visitUris(visitor);
                    break;
                // TODO(b/281044385): Should we do anything about type BUNDLE?
            }
        }
@@ -6983,6 +6995,20 @@ public class RemoteViews implements Parcelable, Filter {
            mElementNames = parcel.createStringArrayList();
        }

        /**
         * See {@link RemoteViews#visitUris(Consumer)}.
         *
         * @hide
         */
        public void visitUris(@NonNull Consumer<Uri> visitor) {
            if (mPendingIntent != null) {
                mPendingIntent.visitUris(visitor);
            }
            if (mFillIntent != null) {
                mFillIntent.visitUris(visitor);
            }
        }

        private void handleViewInteraction(
                View v,
                InteractionHandler handler) {
+27 −3
Original line number Diff line number Diff line
@@ -34,9 +34,6 @@ import android.app.PendingIntent;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
@@ -836,6 +833,33 @@ public class RemoteViewsTest {
        verify(visitor, times(1)).accept(eq(icon4S.getUri()));
    }

    @Test
    public void visitUris_intents() {
        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);

        Uri fillIntentUri = Uri.parse("content://intent/fill");
        views.setOnCheckedChangeResponse(
                R.id.layout,
                RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri)));

        Uri pendingIntentUri = Uri.parse("content://intent/pending");
        PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri);
        views.setOnClickResponse(
                R.id.layout,
                RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent));

        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
        views.visitUris(visitor);
        verify(visitor, times(1)).accept(eq(fillIntentUri));
        verify(visitor, times(1)).accept(eq(pendingIntentUri));
    }

    private PendingIntent getPendingIntentWithUri(Uri uri) {
        return PendingIntent.getActivity(mContext, 0,
                new Intent("action", uri),
                PendingIntent.FLAG_IMMUTABLE);
    }

    @Test
    public void layoutInflaterFactory_nothingSet_returnsNull() {
        final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
Loading