From 0236d1c5cf434af7f0b1cd71b47316496d1faeca Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Tue, 30 Aug 2022 07:54:57 -0400 Subject: [PATCH] Work profile screenshots: save to owner When a work profile screenshot is taken, propagate the owner UserHandle through to the Image export step. This change will write the image to the correct user profile and generate a URI with the userId embedded within using the form: content://10@media/external/images/media/000001.png Since URIs are user-specific, but not unique among users the userId prefix will carry this information through to downstream consumers. The ID can easily be retrieved from the URI using: int userId = ContentProvider.getUserIdFromUri(uri) If there is no userId within the URI, the return value will be the same as Process.myUserHandle(), the user of the current process. Because of this, an explicit UserHandle may not be needed if this information is conveyed entirely within the URI (it is included in this CL as-is). All behavior changes arising from these changes are currently gated on the SysUi flags: SCREENSHOT_REQUEST_PROCESSOR SCREENSHOT_WORK_PROFILE_POLICY This change does not include support for long screenshots nor fixes for screenshot actions (share, edit, etc). Bug: 159422805 Test: atest ImageExporterTest Change-Id: I641544f6a1b4685c743e7983d0af156e32796f56 --- .../systemui/screenshot/ImageExporter.java | 50 +++++++---------- .../screenshot/LongScreenshotActivity.java | 5 +- .../screenshot/SaveImageInBackgroundTask.java | 16 ++++-- .../screenshot/ScreenshotController.java | 53 +++++++++++++------ .../screenshot/ScreenshotPolicyImpl.kt | 4 +- .../screenshot/ScrollCaptureController.java | 1 + .../screenshot/ImageExporterTest.java | 4 +- ...creenshotNotificationSmartActionsTest.java | 6 +-- 8 files changed, 83 insertions(+), 56 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java index 55602a98b8c57..e3658defc52a2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java @@ -19,6 +19,7 @@ package com.android.systemui.screenshot; import static android.os.FileUtils.closeQuietly; import android.annotation.IntRange; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.graphics.Bitmap; @@ -29,6 +30,7 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.os.Trace; +import android.os.UserHandle; import android.provider.MediaStore; import android.util.Log; @@ -142,8 +144,9 @@ class ImageExporter { * * @return a listenable future result */ - ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap) { - return export(executor, requestId, bitmap, ZonedDateTime.now()); + ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap, + UserHandle owner) { + return export(executor, requestId, bitmap, ZonedDateTime.now(), owner); } /** @@ -155,10 +158,10 @@ class ImageExporter { * @return a listenable future result */ ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap, - ZonedDateTime captureTime) { + ZonedDateTime captureTime, UserHandle owner) { final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat, - mQuality, /* publish */ true); + mQuality, /* publish */ true, owner); return CallbackToFutureAdapter.getFuture( (completer) -> { @@ -174,28 +177,6 @@ class ImageExporter { ); } - /** - * Delete the entry. - * - * @param executor the thread for execution - * @param uri the uri of the image to publish - * - * @return a listenable future result - */ - ListenableFuture delete(Executor executor, Uri uri) { - return CallbackToFutureAdapter.getFuture((completer) -> { - executor.execute(() -> { - mResolver.delete(uri, null); - - Result result = new Result(); - result.uri = uri; - result.deleted = true; - completer.set(result); - }); - return "ContentResolver#delete"; - }); - } - static class Result { Uri uri; UUID requestId; @@ -203,7 +184,6 @@ class ImageExporter { long timestamp; CompressFormat format; boolean published; - boolean deleted; @Override public String toString() { @@ -214,7 +194,6 @@ class ImageExporter { sb.append(", timestamp=").append(timestamp); sb.append(", format=").append(format); sb.append(", published=").append(published); - sb.append(", deleted=").append(deleted); sb.append('}'); return sb.toString(); } @@ -227,17 +206,19 @@ class ImageExporter { private final ZonedDateTime mCaptureTime; private final CompressFormat mFormat; private final int mQuality; + private final UserHandle mOwner; private final String mFileName; private final boolean mPublish; Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime, - CompressFormat format, int quality, boolean publish) { + CompressFormat format, int quality, boolean publish, UserHandle owner) { mResolver = resolver; mRequestId = requestId; mBitmap = bitmap; mCaptureTime = captureTime; mFormat = format; mQuality = quality; + mOwner = owner; mFileName = createFilename(mCaptureTime, mFormat); mPublish = publish; } @@ -253,7 +234,7 @@ class ImageExporter { start = Instant.now(); } - uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName); + uri = createEntry(mResolver, mFormat, mCaptureTime, mFileName, mOwner); throwIfInterrupted(); writeImage(mResolver, mBitmap, mFormat, mQuality, uri); @@ -297,15 +278,20 @@ class ImageExporter { } private static Uri createEntry(ContentResolver resolver, CompressFormat format, - ZonedDateTime time, String fileName) throws ImageExportException { + ZonedDateTime time, String fileName, UserHandle owner) throws ImageExportException { Trace.beginSection("ImageExporter_createEntry"); try { final ContentValues values = createMetadata(time, format, fileName); - Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + Uri baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + if (UserHandle.myUserId() != owner.getIdentifier()) { + baseUri = ContentProvider.maybeAddUserId(baseUri, owner.getIdentifier()); + } + Uri uri = resolver.insert(baseUri, values); if (uri == null) { throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL); } + Log.d(TAG, "Inserted new URI: " + uri); return uri; } finally { Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index ba6e98e79ac0b..8bf956b866831 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -30,6 +30,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; @@ -387,7 +388,9 @@ public class LongScreenshotActivity extends Activity { mOutputBitmap = renderBitmap(drawable, bounds); ListenableFuture exportFuture = mImageExporter.export( - mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now()); + mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now(), + // TODO: Owner must match the owner of the captured window. + Process.myUserHandle()); exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index f248d69138785..077ad35fd63fa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -48,6 +48,8 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.google.common.util.concurrent.ListenableFuture; @@ -71,6 +73,7 @@ class SaveImageInBackgroundTask extends AsyncTask { private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"; private final Context mContext; + private FeatureFlags mFlags; private final ScreenshotSmartActions mScreenshotSmartActions; private final ScreenshotController.SaveImageInBackgroundData mParams; private final ScreenshotController.SavedImageData mImageData; @@ -84,7 +87,10 @@ class SaveImageInBackgroundTask extends AsyncTask { private final ImageExporter mImageExporter; private long mImageTime; - SaveImageInBackgroundTask(Context context, ImageExporter exporter, + SaveImageInBackgroundTask( + Context context, + FeatureFlags flags, + ImageExporter exporter, ScreenshotSmartActions screenshotSmartActions, ScreenshotController.SaveImageInBackgroundData data, Supplier sharedElementTransition, @@ -92,6 +98,7 @@ class SaveImageInBackgroundTask extends AsyncTask { screenshotNotificationSmartActionsProvider ) { mContext = context; + mFlags = flags; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); mQuickShareData = new ScreenshotController.QuickShareData(); @@ -117,7 +124,8 @@ class SaveImageInBackgroundTask extends AsyncTask { } // TODO: move to constructor / from ScreenshotRequest final UUID requestId = UUID.randomUUID(); - final UserHandle user = getUserHandleOfForegroundApplication(mContext); + final UserHandle user = mFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY) + ? mParams.owner : getUserHandleOfForegroundApplication(mContext); Thread.currentThread().setPriority(Thread.MAX_PRIORITY); @@ -133,8 +141,9 @@ class SaveImageInBackgroundTask extends AsyncTask { // Call synchronously here since already on a background thread. ListenableFuture future = - mImageExporter.export(Runnable::run, requestId, image); + mImageExporter.export(Runnable::run, requestId, image, mParams.owner); ImageExporter.Result result = future.get(); + Log.d(TAG, "Saved screenshot: " + result); final Uri uri = result.uri; mImageTime = result.timestamp; @@ -157,6 +166,7 @@ class SaveImageInBackgroundTask extends AsyncTask { } mImageData.uri = uri; + mImageData.owner = user; mImageData.smartActions = smartActions; mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 3fee232b34658..df32d2081fde4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -34,6 +34,7 @@ import static java.util.Objects.requireNonNull; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -57,7 +58,9 @@ import android.media.AudioSystem; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; @@ -90,6 +93,7 @@ import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.clipboardoverlay.ClipboardOverlayController; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.android.systemui.util.Assert; @@ -151,6 +155,7 @@ public class ScreenshotController { public Consumer finisher; public ScreenshotController.ActionsReadyListener mActionsReadyListener; public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener; + public UserHandle owner; void clearImage() { image = null; @@ -167,6 +172,8 @@ public class ScreenshotController { public Notification.Action deleteAction; public List smartActions; public Notification.Action quickShareAction; + public UserHandle owner; + /** * POD for shared element transition. @@ -242,6 +249,7 @@ public class ScreenshotController { private static final int SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS = 6000; private final WindowContext mContext; + private final FeatureFlags mFlags; private final ScreenshotNotificationsController mNotificationsController; private final ScreenshotSmartActions mScreenshotSmartActions; private final UiEventLogger mUiEventLogger; @@ -288,6 +296,7 @@ public class ScreenshotController { @Inject ScreenshotController( Context context, + FeatureFlags flags, ScreenshotSmartActions screenshotSmartActions, ScreenshotNotificationsController screenshotNotificationsController, ScrollCaptureClient scrollCaptureClient, @@ -331,6 +340,7 @@ public class ScreenshotController { final Context displayContext = context.createDisplayContext(getDefaultDisplay()); mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null); mWindowManager = mContext.getSystemService(WindowManager.class); + mFlags = flags; mAccessibilityManager = AccessibilityManager.getInstance(mContext); @@ -377,7 +387,6 @@ public class ScreenshotController { void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds, Insets visibleInsets, int taskId, int userId, ComponentName topComponent, Consumer finisher, RequestCallback requestCallback) { - // TODO: use task Id, userId, topComponent for smart handler Assert.isMainThread(); if (screenshot == null) { Log.e(TAG, "Got null bitmap from screenshot message"); @@ -395,7 +404,7 @@ public class ScreenshotController { } mCurrentRequestCallback = requestCallback; saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent, - showFlash); + showFlash, UserHandle.of(userId)); } /** @@ -543,14 +552,15 @@ public class ScreenshotController { return; } - saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true); + saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true, + Process.myUserHandle()); mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION), ClipboardOverlayController.SELF_PERMISSION); } private void saveScreenshot(Bitmap screenshot, Consumer finisher, Rect screenRect, - Insets screenInsets, ComponentName topComponent, boolean showFlash) { + Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) { withWindowAttached(() -> mScreenshotView.announceForAccessibility( mContext.getResources().getString(R.string.screenshot_saving_title))); @@ -575,11 +585,11 @@ public class ScreenshotController { mScreenBitmap = screenshot; - if (!isUserSetupComplete()) { + if (!isUserSetupComplete(owner)) { Log.w(TAG, "User setup not complete, displaying toast only"); // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing // and sharing shouldn't be exposed to the user. - saveScreenshotAndToast(finisher); + saveScreenshotAndToast(owner, finisher); return; } @@ -587,7 +597,7 @@ public class ScreenshotController { mScreenBitmap.setHasAlpha(false); mScreenBitmap.prepareToDraw(); - saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady, + saveScreenshotInWorkerThread(owner, finisher, this::showUiOnActionsReady, this::showUiOnQuickShareActionReady); // The window is focusable by default @@ -853,11 +863,12 @@ public class ScreenshotController { * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on * failure). */ - private void saveScreenshotAndToast(Consumer finisher) { + private void saveScreenshotAndToast(UserHandle owner, Consumer finisher) { // Play the shutter sound to notify that we've taken a screenshot playCameraSound(); saveScreenshotInWorkerThread( + owner, /* onComplete */ finisher, /* actionsReadyListener */ imageData -> { if (DEBUG_CALLBACK) { @@ -925,9 +936,11 @@ public class ScreenshotController { /** * Creates a new worker thread and saves the screenshot to the media store. */ - private void saveScreenshotInWorkerThread(Consumer finisher, - @Nullable ScreenshotController.ActionsReadyListener actionsReadyListener, - @Nullable ScreenshotController.QuickShareActionReadyListener + private void saveScreenshotInWorkerThread( + UserHandle owner, + @NonNull Consumer finisher, + @Nullable ActionsReadyListener actionsReadyListener, + @Nullable QuickShareActionReadyListener quickShareActionsReadyListener) { ScreenshotController.SaveImageInBackgroundData data = new ScreenshotController.SaveImageInBackgroundData(); @@ -935,13 +948,14 @@ public class ScreenshotController { data.finisher = finisher; data.mActionsReadyListener = actionsReadyListener; data.mQuickShareActionsReadyListener = quickShareActionsReadyListener; + data.owner = owner; if (mSaveInBgTask != null) { // just log success/failure for the pre-existing screenshot mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady); } - mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, + mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mFlags, mImageExporter, mScreenshotSmartActions, data, getActionTransitionSupplier(), mScreenshotNotificationSmartActionsProvider); mSaveInBgTask.execute(); @@ -960,6 +974,15 @@ public class ScreenshotController { mScreenshotHandler.resetTimeout(); if (imageData.uri != null) { + if (!imageData.owner.equals(Process.myUserHandle())) { + // TODO: Handle non-primary user ownership (e.g. Work Profile) + // This image is owned by another user. Special treatment will be + // required in the UI (badging) as well as sending intents which can + // correctly forward those URIs on to be read (actions). + + Log.d(TAG, "*** Screenshot saved to a non-primary user (" + + imageData.owner + ") as " + imageData.uri); + } mScreenshotHandler.post(() -> { if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { mScreenshotAnimation.addListener(new AnimatorListenerAdapter() { @@ -1033,9 +1056,9 @@ public class ScreenshotController { } } - private boolean isUserSetupComplete() { - return Settings.Secure.getInt(mContext.getContentResolver(), - SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; + private boolean isUserSetupComplete(UserHandle owner) { + return Settings.Secure.getInt(mContext.createContextAsUser(owner, 0) + .getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1; } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt index c2a50609b6a5a..3a35286063029 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt @@ -68,7 +68,9 @@ internal open class ScreenshotPolicyImpl @Inject constructor( } override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean { - return withContext(bgDispatcher) { userMgr.isManagedProfile(userId) } + val managed = withContext(bgDispatcher) { userMgr.isManagedProfile(userId) } + Log.d(TAG, "isManagedProfile: $managed") + return managed } private fun nonPipVisibleTask(info: RootTaskInfo): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 83b60fb23b90b..30a0b8f2d76f5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -78,6 +78,7 @@ public class ScrollCaptureController { static class LongScreenshot { private final ImageTileSet mImageTileSet; private final Session mSession; + // TODO: Add UserHandle so LongScreenshots can adhere to work profile screenshot policy LongScreenshot(Session session, ImageTileSet imageTileSet) { mSession = session; diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java index 7d563399ee1c6..4c44dacab1a21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java @@ -33,6 +33,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.os.Build; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.provider.MediaStore; import android.testing.AndroidTestingRunner; @@ -97,7 +98,8 @@ public class ImageExporterTest extends SysuiTestCase { Bitmap original = createCheckerBitmap(10, 10, 10); ListenableFuture direct = - exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME); + exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME, + Process.myUserHandle()); assertTrue("future should be done", direct.isDone()); assertFalse("future should not be canceled", direct.isCancelled()); ImageExporter.Result result = direct.get(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 69b7b88b0524f..8c9404e336ca8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -180,7 +180,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.finisher = null; data.mActionsReadyListener = null; SaveImageInBackgroundTask task = - new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, + new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data, ActionTransition::new, mSmartActionsProvider); Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), @@ -208,7 +208,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.finisher = null; data.mActionsReadyListener = null; SaveImageInBackgroundTask task = - new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, + new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data, ActionTransition::new, mSmartActionsProvider); Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), @@ -236,7 +236,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.finisher = null; data.mActionsReadyListener = null; SaveImageInBackgroundTask task = - new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, + new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data, ActionTransition::new, mSmartActionsProvider); Notification.Action deleteAction = task.createDeleteAction(mContext,