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

Commit 7541b339 authored by Ajinkya Chalke's avatar Ajinkya Chalke Committed by Automerger Merge Worker
Browse files

Merge "Implement work profile considerations in app clips" into udc-dev am:...

Merge "Implement work profile considerations in app clips" into udc-dev am: 5d63998d am: 685777b3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/22930877



Change-Id: I992e0b3bbf3f8840ac2b363da880547b321c5ba0
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 7817d6fa 685777b3
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -293,10 +293,9 @@ public class ImageExporter {
            final ContentValues values = createMetadata(time, format, fileName);

            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);
            Uri uriWithUserId = ContentProvider.maybeAddUserId(baseUri, owner.getIdentifier());

            Uri uri = resolver.insert(uriWithUserId, values);
            if (uri == null) {
                throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL);
            }
+6 −1
Original line number Diff line number Diff line
@@ -247,7 +247,7 @@ public class AppClipsActivity extends ComponentActivity {
        }

        updateImageDimensions();
        mViewModel.saveScreenshotThenFinish(drawable, bounds);
        mViewModel.saveScreenshotThenFinish(drawable, bounds, getUser());
    }

    private void setResultThenFinish(Uri uri) {
@@ -255,6 +255,11 @@ public class AppClipsActivity extends ComponentActivity {
            return;
        }

        // Grant permission here instead of in the trampoline activity because this activity can run
        // as work profile user so the URI can belong to the work profile user while the trampoline
        // activity always runs as main user.
        grantUriPermission(mCallingPackageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

        Bundle data = new Bundle();
        data.putInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE,
                Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
+14 −5
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package com.android.systemui.screenshot.appclips;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.view.Display;
import android.os.UserManager;

import androidx.annotation.Nullable;

@@ -27,6 +27,7 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.settings.DisplayTracker;

import javax.inject.Inject;

@@ -35,14 +36,20 @@ import javax.inject.Inject;
class AppClipsCrossProcessHelper {

    private final ServiceConnector<IAppClipsScreenshotHelperService> mProxyConnector;
    private final DisplayTracker mDisplayTracker;

    @Inject
    AppClipsCrossProcessHelper(@Application Context context) {
        mProxyConnector = new ServiceConnector.Impl<IAppClipsScreenshotHelperService>(context,
    AppClipsCrossProcessHelper(@Application Context context, UserManager userManager,
            DisplayTracker displayTracker) {
        // Start a service as main user so that even if the app clips activity is running as work
        // profile user the service is able to use correct instance of Bubbles to grab a screenshot
        // excluding the bubble layer.
        mProxyConnector = new ServiceConnector.Impl<>(context,
                new Intent(context, AppClipsScreenshotHelperService.class),
                Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
                        | Context.BIND_NOT_VISIBLE, context.getUserId(),
                        | Context.BIND_NOT_VISIBLE, userManager.getMainUser().getIdentifier(),
                IAppClipsScreenshotHelperService.Stub::asInterface);
        mDisplayTracker = displayTracker;
    }

    /**
@@ -56,7 +63,9 @@ class AppClipsCrossProcessHelper {
        try {
            AndroidFuture<ScreenshotHardwareBufferInternal> future =
                    mProxyConnector.postForResult(
                            service -> service.takeScreenshot(Display.DEFAULT_DISPLAY));
                            service ->
                                    // Take a screenshot of the default display of the user.
                                    service.takeScreenshot(mDisplayTracker.getDefaultDisplayId()));
            return future.get().createBitmapThenCloseBuffer();
        } catch (Exception e) {
            return null;
+28 −8
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED;
import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;

import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
@@ -34,6 +33,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
@@ -82,6 +82,8 @@ public class AppClipsTrampolineActivity extends Activity {
    private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName();
    static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
    static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI";
    @VisibleForTesting
    static final String EXTRA_USE_WP_USER = TAG + "USE_WP_USER";
    static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE";
    static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER";
    static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME";
@@ -98,6 +100,7 @@ public class AppClipsTrampolineActivity extends Activity {
    private final ResultReceiver mResultReceiver;

    private Intent mKillAppClipsBroadcastIntent;
    private UserHandle mNotesAppUser;

    @Inject
    public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags,
@@ -165,15 +168,21 @@ public class AppClipsTrampolineActivity extends Activity {
            return;
        }

        mNotesAppUser = getUser();
        if (getIntent().getBooleanExtra(EXTRA_USE_WP_USER, /* defaultValue= */ false)) {
            // Get the work profile user internally instead of passing around via intent extras as
            // this activity is exported apps could potentially mess around with intent extras.
            mNotesAppUser = getWorkProfileUser().orElse(mNotesAppUser);
        }

        String callingPackageName = getCallingPackage();
        Intent intent = new Intent().setComponent(componentName)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver)
                .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName);

        try {
            // Start the App Clips activity.
            startActivity(intent);
            // Start the App Clips activity for the user corresponding to the notes app user.
            startActivityAsUser(intent, mNotesAppUser);

            // Set up the broadcast intent that will inform the above App Clips activity to finish
            // when this trampoline activity is finished.
@@ -198,6 +207,13 @@ public class AppClipsTrampolineActivity extends Activity {
        }
    }

    private Optional<UserHandle> getWorkProfileUser() {
        return mUserTracker.getUserProfiles().stream()
                .filter(profile -> mUserManager.isManagedProfile(profile.id))
                .findFirst()
                .map(UserInfo::getUserHandle);
    }

    private void maybeStartActivityForWPUser() {
        UserHandle mainUser = mUserManager.getMainUser();
        if (mainUser == null) {
@@ -205,9 +221,13 @@ public class AppClipsTrampolineActivity extends Activity {
            return;
        }

        // Start the activity as the main user with activity result forwarding.
        // Start the activity as the main user with activity result forwarding. Set the intent extra
        // so that the newly started trampoline activity starts the actual app clips activity as the
        // work profile user. Starting the app clips activity as the work profile user is required
        // to save the screenshot in work profile user storage and grant read permission to the URI.
        startActivityAsUser(
                new Intent(this, AppClipsTrampolineActivity.class)
                        .putExtra(EXTRA_USE_WP_USER, /* value= */ true)
                        .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT), mainUser);
    }

@@ -221,7 +241,7 @@ public class AppClipsTrampolineActivity extends Activity {
        int callingPackageUid = 0;
        try {
            callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName,
                    APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid;
                    APPLICATION_INFO_FLAGS, mNotesAppUser.getIdentifier()).uid;
        } catch (NameNotFoundException e) {
            Log.d(TAG, "Couldn't find notes app UID " + e);
        }
@@ -254,14 +274,14 @@ public class AppClipsTrampolineActivity extends Activity {

            if (statusCode == CAPTURE_CONTENT_FOR_NOTE_SUCCESS) {
                Uri uri = resultData.getParcelable(EXTRA_SCREENSHOT_URI, Uri.class);
                convertedData.setData(uri).addFlags(FLAG_GRANT_READ_URI_PERMISSION);
                convertedData.setData(uri);
            }

            // Broadcast no longer required, setting it to null.
            mKillAppClipsBroadcastIntent = null;

            // Expand the note bubble before returning the result.
            mNoteTaskController.showNoteTask(NoteTaskEntryPoint.APP_CLIPS);
            mNoteTaskController.showNoteTaskAsUser(NoteTaskEntryPoint.APP_CLIPS, mNotesAppUser);
            setResult(RESULT_OK, convertedData);
            finish();
        }
+3 −5
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import android.graphics.Rect;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
@@ -110,16 +110,14 @@ final class AppClipsViewModel extends ViewModel {
     * Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
     * {@link LiveData}.
     */
    void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
    void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds, UserHandle user) {
        mBgExecutor.execute(() -> {
            // Render the screenshot bitmap in background.
            Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds);

            // Export and save the screenshot in background.
            // TODO(b/267310185): Save to work profile UserHandle.
            ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
                    mBgExecutor, UUID.randomUUID(), screenshotBitmap,
                    Process.myUserHandle());
                    mBgExecutor, UUID.randomUUID(), screenshotBitmap, user);

            // Get the result and update state on main thread.
            exportFuture.addListener(() -> {
Loading