[DO NOT MERGE] Update quickshare intent rather than recreating

Currently, we extract the quickshare intent and re-wrap it as a new
PendingIntent once we get the screenshot URI. This is insecure as
it leads to executing the original with SysUI's permissions, which
the app may not have. This change switches to using Intent.fillin
to add the URI, keeping the original PendingIntent and original
permission set.

Bug: 278720336
Fix: 278720336
Test: manual (to test successful quickshare), atest
SaveImageInBackgroundTaskTest (to verify original pending intent
unchanged)
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:589ce3909c6e9e30f073df86e7de4503854a032a)
Merged-In: Icad3d5f939fcfb894e2038948954bc2735dbe326
Change-Id: Icad3d5f939fcfb894e2038948954bc2735dbe326
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index bf5fbd2..baf765d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -141,7 +141,12 @@
                 // Since Quick Share target recommendation does not rely on image URL, it is
                 // queried and surfaced before image compress/export. Action intent would not be
                 // used, because it does not contain image URL.
-                queryQuickShareAction(image, user);
+                Notification.Action quickShare =
+                        queryQuickShareAction(mScreenshotId, image, user, null);
+                if (quickShare != null) {
+                    mQuickShareData.quickShareAction = quickShare;
+                    mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData);
+                }
             }
 
             // Call synchronously here since already on a background thread.
@@ -180,9 +185,10 @@
                     smartActionsEnabled);
             mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri,
                     smartActionsEnabled);
-            mImageData.quickShareAction = createQuickShareAction(mContext,
-                    mQuickShareData.quickShareAction, uri);
-            mImageData.subject = getSubjectString();
+            mImageData.quickShareAction = createQuickShareAction(
+                    mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image,
+                    user);
+            mImageData.subject = getSubjectString(mImageTime);
 
             mParams.mActionsReadyListener.onActionsReady(mImageData);
             if (DEBUG_CALLBACK) {
@@ -257,7 +263,7 @@
                     new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
                     new ClipData.Item(uri));
             sharingIntent.setClipData(clipdata);
-            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getSubjectString());
+            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getSubjectString(mImageTime));
             sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                     .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
@@ -423,60 +429,73 @@
     }
 
     /**
-     * Populate image uri into intent of Quick Share action.
+     * Wrap the quickshare intent and populate the fillin intent with the URI
      */
     @VisibleForTesting
-    private Notification.Action createQuickShareAction(Context context, Notification.Action action,
-            Uri uri) {
-        if (action == null) {
+    Notification.Action createQuickShareAction(
+            Notification.Action quickShare, String screenshotId, Uri uri, long imageTime,
+            Bitmap image, UserHandle user) {
+        if (quickShare == null) {
             return null;
+        } else if (quickShare.actionIntent.isImmutable()) {
+            Notification.Action quickShareWithUri =
+                    queryQuickShareAction(screenshotId, image, user, uri);
+            if (quickShareWithUri == null
+                    || !quickShareWithUri.title.toString().contentEquals(quickShare.title)) {
+                return null;
+            }
+            quickShare = quickShareWithUri;
         }
-        // Populate image URI into Quick Share chip intent
-        Intent sharingIntent = action.actionIntent.getIntent();
-        sharingIntent.setType("image/png");
-        sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
-        String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
-        String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
-        sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
-        // Include URI in ClipData also, so that grantPermission picks it up.
-        // We don't use setData here because some apps interpret this as "to:".
-        ClipData clipdata = new ClipData(new ClipDescription("content",
-                new String[]{"image/png"}),
-                new ClipData.Item(uri));
-        sharingIntent.setClipData(clipdata);
-        sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        PendingIntent updatedPendingIntent = PendingIntent.getActivity(
-                context, 0, sharingIntent,
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
-        // Proxy smart actions through {@link SmartActionsReceiver} for logging smart actions.
-        Bundle extras = action.getExtras();
+        Intent wrappedIntent = new Intent(mContext, SmartActionsReceiver.class)
+                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent)
+                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT_FILLIN,
+                        createFillInIntent(uri, imageTime))
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        Bundle extras = quickShare.getExtras();
         String actionType = extras.getString(
                 ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
                 ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
-        Intent intent = new Intent(context, SmartActionsReceiver.class)
-                .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, updatedPendingIntent)
-                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         // We only query for quick share actions when smart actions are enabled, so we can assert
         // that it's true here.
-        addIntentExtras(mScreenshotId, intent, actionType, true /* smartActionsEnabled */);
-        PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
-                mRandom.nextInt(),
-                intent,
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-        return new Notification.Action.Builder(action.getIcon(), action.title,
-                broadcastIntent).setContextual(true).addExtras(extras).build();
+        addIntentExtras(screenshotId, wrappedIntent, actionType, true /* smartActionsEnabled */);
+        PendingIntent broadcastIntent =
+                PendingIntent.getBroadcast(mContext, mRandom.nextInt(), wrappedIntent,
+                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+        return new Notification.Action.Builder(quickShare.getIcon(), quickShare.title,
+                broadcastIntent)
+                .setContextual(true)
+                .addExtras(extras)
+                .build();
+    }
+
+    private Intent createFillInIntent(Uri uri, long imageTime) {
+        Intent fillIn = new Intent();
+        fillIn.setType("image/png");
+        fillIn.putExtra(Intent.EXTRA_STREAM, uri);
+        fillIn.putExtra(Intent.EXTRA_SUBJECT, getSubjectString(imageTime));
+        // Include URI in ClipData also, so that grantPermission picks it up.
+        // We don't use setData here because some apps interpret this as "to:".
+        ClipData clipData = new ClipData(
+                new ClipDescription("content", new String[]{"image/png"}),
+                new ClipData.Item(uri));
+        fillIn.setClipData(clipData);
+        fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        return fillIn;
     }
 
     /**
      * Query and surface Quick Share chip if it is available. Action intent would not be used,
      * because it does not contain image URL which would be populated in {@link
-     * #createQuickShareAction(Context, Notification.Action, Uri)}
+     * #createQuickShareAction(Notification.Action, String, Uri, long, Bitmap, UserHandle)}
      */
-    private void queryQuickShareAction(Bitmap image, UserHandle user) {
+
+    @VisibleForTesting
+    Notification.Action queryQuickShareAction(
+            String screenshotId, Bitmap image, UserHandle user, Uri uri) {
         CompletableFuture<List<Notification.Action>> quickShareActionsFuture =
                 mScreenshotSmartActions.getSmartActionsFuture(
-                        mScreenshotId, null, image, mSmartActionsProvider,
+                        screenshotId, uri, image, mSmartActionsProvider,
                         ScreenshotSmartActionType.QUICK_SHARE_ACTION,
                         true /* smartActionsEnabled */, user);
         int timeoutMs = DeviceConfig.getInt(
@@ -485,17 +504,17 @@
                 500);
         List<Notification.Action> quickShareActions =
                 mScreenshotSmartActions.getSmartActions(
-                        mScreenshotId, quickShareActionsFuture, timeoutMs,
+                        screenshotId, quickShareActionsFuture, timeoutMs,
                         mSmartActionsProvider,
                         ScreenshotSmartActionType.QUICK_SHARE_ACTION);
         if (!quickShareActions.isEmpty()) {
-            mQuickShareData.quickShareAction = quickShareActions.get(0);
-            mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData);
+            return quickShareActions.get(0);
         }
+        return null;
     }
 
-    private String getSubjectString() {
-        String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
+    private static String getSubjectString(long imageTime) {
+        String subjectDate = DateFormat.getDateTimeInstance().format(new Date(imageTime));
         return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 9f4929a..c291f87 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -246,6 +246,7 @@
     static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
     static final String EXTRA_OVERRIDE_TRANSITION = "android:screenshot_override_transition";
     static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+    static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin";
 
     static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
     static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 68b46d2..ca713fe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -30,7 +30,6 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
@@ -61,7 +60,6 @@
                 screenshotNotificationSmartActionsProviderProvider;
     }
 
-    @VisibleForTesting
     CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
             String screenshotId, Uri screenshotUri, Bitmap image,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
@@ -83,7 +81,7 @@
         if (image.getConfig() != Bitmap.Config.HARDWARE) {
             if (DEBUG_ACTIONS) {
                 Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. "
-                                + "Returning empty list.", image.getConfig()));
+                        + "Returning empty list.", image.getConfig()));
             }
             return CompletableFuture.completedFuture(Collections.emptyList());
         }
@@ -112,7 +110,6 @@
         return smartActionsFuture;
     }
 
-    @VisibleForTesting
     List<Notification.Action> getSmartActions(String screenshotId,
             CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index 45af187..9761f59 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
 import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
+import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT_FILLIN;
 import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
 import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
 
@@ -46,7 +47,9 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
+        PendingIntent pendingIntent =
+                intent.getParcelableExtra(EXTRA_ACTION_INTENT, PendingIntent.class);
+        Intent fillIn = intent.getParcelableExtra(EXTRA_ACTION_INTENT_FILLIN, Intent.class);
         String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
         if (DEBUG_ACTIONS) {
             Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
@@ -54,7 +57,7 @@
         ActivityOptions opts = ActivityOptions.makeBasic();
 
         try {
-            pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
+            pendingIntent.send(context, 0, fillIn, null, null, null, opts.toBundle());
         } catch (PendingIntent.CanceledException e) {
             Log.e(TAG, "Pending intent canceled", e);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
new file mode 100644
index 0000000..fbb77cd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
@@ -0,0 +1,283 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.UserHandle
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData
+import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import java.util.concurrent.CompletableFuture
+import java.util.function.Supplier
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class SaveImageInBackgroundTaskTest : SysuiTestCase() {
+    private val imageExporter = mock<ImageExporter>()
+    private val smartActions = mock<ScreenshotSmartActions>()
+    private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
+    private val saveImageData = SaveImageInBackgroundData()
+    private val sharedTransitionSupplier =
+        mock<Supplier<ScreenshotController.SavedImageData.ActionTransition>>()
+    private val testScreenshotId: String = "testScreenshotId"
+    private val testBitmap = mock<Bitmap>()
+    private val testUser = UserHandle.getUserHandleForUid(0)
+    private val testIcon = mock<Icon>()
+    private val testImageTime = 1234.toLong()
+    private val flags = FakeFeatureFlags()
+
+    private val smartActionsUriFuture = mock<CompletableFuture<List<Notification.Action>>>()
+    private val smartActionsFuture = mock<CompletableFuture<List<Notification.Action>>>()
+
+    private val testUri: Uri = Uri.parse("testUri")
+    private val intent =
+        Intent(Intent.ACTION_SEND)
+            .setComponent(
+                ComponentName.unflattenFromString(
+                    "com.google.android.test/com.google.android.test.TestActivity"
+                )
+            )
+    private val immutablePendingIntent =
+        PendingIntent.getBroadcast(
+            mContext,
+            0,
+            intent,
+            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
+        )
+    private val mutablePendingIntent =
+        PendingIntent.getBroadcast(
+            mContext,
+            0,
+            intent,
+            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
+        )
+
+    private val saveImageTask =
+        SaveImageInBackgroundTask(
+            mContext,
+            flags,
+            imageExporter,
+            smartActions,
+            saveImageData,
+            sharedTransitionSupplier,
+            smartActionsProvider,
+        )
+
+    @Before
+    fun setup() {
+        whenever(
+                smartActions.getSmartActionsFuture(
+                    eq(testScreenshotId),
+                    any(Uri::class.java),
+                    eq(testBitmap),
+                    eq(smartActionsProvider),
+                    any(ScreenshotSmartActionType::class.java),
+                    any(Boolean::class.java),
+                    eq(testUser)
+                )
+            )
+            .thenReturn(smartActionsUriFuture)
+        whenever(
+                smartActions.getSmartActionsFuture(
+                    eq(testScreenshotId),
+                    eq(null),
+                    eq(testBitmap),
+                    eq(smartActionsProvider),
+                    any(ScreenshotSmartActionType::class.java),
+                    any(Boolean::class.java),
+                    eq(testUser)
+                )
+            )
+            .thenReturn(smartActionsFuture)
+    }
+
+    @Test
+    fun testQueryQuickShare_noAction() {
+        whenever(
+                smartActions.getSmartActions(
+                    eq(testScreenshotId),
+                    eq(smartActionsFuture),
+                    any(Int::class.java),
+                    eq(smartActionsProvider),
+                    eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
+                )
+            )
+            .thenReturn(ArrayList<Notification.Action>())
+
+        val quickShareAction =
+            saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)
+
+        assertNull(quickShareAction)
+    }
+
+    @Test
+    fun testQueryQuickShare_withActions() {
+        val actions = ArrayList<Notification.Action>()
+        actions.add(constructAction("Action One", mutablePendingIntent))
+        actions.add(constructAction("Action Two", mutablePendingIntent))
+        whenever(
+                smartActions.getSmartActions(
+                    eq(testScreenshotId),
+                    eq(smartActionsUriFuture),
+                    any(Int::class.java),
+                    eq(smartActionsProvider),
+                    eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
+                )
+            )
+            .thenReturn(actions)
+
+        val quickShareAction =
+            saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)!!
+
+        assertEquals("Action One", quickShareAction.title)
+        assertEquals(mutablePendingIntent, quickShareAction.actionIntent)
+    }
+
+    @Test
+    fun testCreateQuickShareAction_originalWasNull_returnsNull() {
+        val quickShareAction =
+            saveImageTask.createQuickShareAction(
+                null,
+                testScreenshotId,
+                testUri,
+                testImageTime,
+                testBitmap,
+                testUser
+            )
+
+        assertNull(quickShareAction)
+    }
+
+    @Test
+    fun testCreateQuickShareAction_immutableIntentDifferentAction_returnsNull() {
+        val actions = ArrayList<Notification.Action>()
+        actions.add(constructAction("New Test Action", immutablePendingIntent))
+        whenever(
+                smartActions.getSmartActions(
+                    eq(testScreenshotId),
+                    eq(smartActionsUriFuture),
+                    any(Int::class.java),
+                    eq(smartActionsProvider),
+                    eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
+                )
+            )
+            .thenReturn(actions)
+        val origAction = constructAction("Old Test Action", immutablePendingIntent)
+
+        val quickShareAction =
+            saveImageTask.createQuickShareAction(
+                origAction,
+                testScreenshotId,
+                testUri,
+                testImageTime,
+                testBitmap,
+                testUser,
+            )
+
+        assertNull(quickShareAction)
+    }
+
+    @Test
+    fun testCreateQuickShareAction_mutableIntent_returnsSafeIntent() {
+        val actions = ArrayList<Notification.Action>()
+        val action = constructAction("Action One", mutablePendingIntent)
+        actions.add(action)
+        whenever(
+                smartActions.getSmartActions(
+                    eq(testScreenshotId),
+                    eq(smartActionsUriFuture),
+                    any(Int::class.java),
+                    eq(smartActionsProvider),
+                    eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
+                )
+            )
+            .thenReturn(actions)
+
+        val quickShareAction =
+            saveImageTask.createQuickShareAction(
+                constructAction("Test Action", mutablePendingIntent),
+                testScreenshotId,
+                testUri,
+                testImageTime,
+                testBitmap,
+                testUser
+            )
+        val quickSharePendingIntent =
+            quickShareAction.actionIntent.intent.extras!!.getParcelable(
+                ScreenshotController.EXTRA_ACTION_INTENT,
+                PendingIntent::class.java
+            )
+
+        assertEquals("Test Action", quickShareAction.title)
+        assertEquals(mutablePendingIntent, quickSharePendingIntent)
+    }
+
+    @Test
+    fun testCreateQuickShareAction_immutableIntent_returnsSafeIntent() {
+        val actions = ArrayList<Notification.Action>()
+        val action = constructAction("Test Action", immutablePendingIntent)
+        actions.add(action)
+        whenever(
+                smartActions.getSmartActions(
+                    eq(testScreenshotId),
+                    eq(smartActionsUriFuture),
+                    any(Int::class.java),
+                    eq(smartActionsProvider),
+                    eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
+                )
+            )
+            .thenReturn(actions)
+
+        val quickShareAction =
+            saveImageTask.createQuickShareAction(
+                constructAction("Test Action", immutablePendingIntent),
+                testScreenshotId,
+                testUri,
+                testImageTime,
+                testBitmap,
+                testUser,
+            )!!
+
+        assertEquals("Test Action", quickShareAction.title)
+        assertEquals(
+            immutablePendingIntent,
+            quickShareAction.actionIntent.intent.extras!!.getParcelable(
+                ScreenshotController.EXTRA_ACTION_INTENT,
+                PendingIntent::class.java
+            )
+        )
+    }
+
+    private fun constructAction(title: String, intent: PendingIntent): Notification.Action {
+        return Notification.Action.Builder(testIcon, title, intent).build()
+    }
+}