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

Commit c7496a3a authored by Zim's avatar Zim
Browse files

Support apps sharing file:// URIs with the camera API

The camera API, MediaStore.ACTION_IMAGE_CAPTURE requires apps to pass
a content:// URI with write permissions to the camera. Unfortunately,
apps haven't been doing this and we only started hitting problems in R
for two reasons:
1. The FileUriExposedException that should crash apps when they try to
share file:// URIs acroos binder is skipped. This is because, the
image_capture intent is passed across binder as a field in a
ChooserActivity Intent and the child intents are not checked for
file URI exposed
2. Prior to R, when camera gets a file:// URI, camera issues a file
open(2) in its process. This open(2) succeeds because the camera had
write_external_storage permission which gave it write access to all
files on external storage

Now, camera targets R and (2) fails because camera does not have write
access to files owned by other apps. To workaround, we do the
following in the apps process when it targets < R:
a. When we detect a file:// URI for the camera in an Intent, we create
the file on disk if it is not already created.
b. Scan the file to insert it in the database and retrieve a
content:// URI
c. Replace the file:// URI with the content URI in the image_capture
intent

This works because, the system will ensure the camera is granted write
access to the content URI.

Test: Manual
Bug: 156336269
Change-Id: I4849ff5e806a8207650ff7534846c36ecdc6d3c0
parent 259d98b1
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -5562,7 +5562,7 @@ public class Activity extends ContextThemeWrapper
            options = transferSpringboardActivityOptions(options);
            String resolvedType = null;
            if (fillInIntent != null) {
                fillInIntent.migrateExtraStreamToClipData();
                fillInIntent.migrateExtraStreamToClipData(this);
                fillInIntent.prepareToLeaveProcess(this);
                resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
            }
@@ -5817,7 +5817,7 @@ public class Activity extends ContextThemeWrapper
                if (referrer != null) {
                    intent.putExtra(Intent.EXTRA_REFERRER, referrer);
                }
                intent.migrateExtraStreamToClipData();
                intent.migrateExtraStreamToClipData(this);
                intent.prepareToLeaveProcess(this);
                result = ActivityTaskManager.getService()
                    .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
@@ -5888,7 +5888,7 @@ public class Activity extends ContextThemeWrapper
            @Nullable Bundle options) {
        if (mParent == null) {
            try {
                intent.migrateExtraStreamToClipData();
                intent.migrateExtraStreamToClipData(this);
                intent.prepareToLeaveProcess(this);
                return ActivityTaskManager.getService()
                    .startNextMatchingActivity(mToken, intent, options);
+1 −1
Original line number Diff line number Diff line
@@ -1087,7 +1087,7 @@ class ContextImpl extends Context {
        try {
            String resolvedType = null;
            if (fillInIntent != null) {
                fillInIntent.migrateExtraStreamToClipData();
                fillInIntent.migrateExtraStreamToClipData(this);
                fillInIntent.prepareToLeaveProcess(this);
                resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
            }
+6 −6
Original line number Diff line number Diff line
@@ -1718,7 +1718,7 @@ public class Instrumentation {
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
@@ -1788,7 +1788,7 @@ public class Instrumentation {
        try {
            String[] resolvedTypes = new String[intents.length];
            for (int i=0; i<intents.length; i++) {
                intents[i].migrateExtraStreamToClipData();
                intents[i].migrateExtraStreamToClipData(who);
                intents[i].prepareToLeaveProcess(who);
                resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
            }
@@ -1857,7 +1857,7 @@ public class Instrumentation {
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
@@ -1924,7 +1924,7 @@ public class Instrumentation {
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivityAsUser(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
@@ -1970,7 +1970,7 @@ public class Instrumentation {
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService()
                .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
@@ -2017,7 +2017,7 @@ public class Instrumentation {
            }
        }
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
                    who.getAttributionTag(), intent,
+4 −4
Original line number Diff line number Diff line
@@ -351,7 +351,7 @@ public final class PendingIntent implements Parcelable {
        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                context.getContentResolver()) : null;
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(context);
            intent.prepareToLeaveProcess(context);
            IIntentSender target =
                ActivityManager.getService().getIntentSenderWithFeature(
@@ -377,7 +377,7 @@ public final class PendingIntent implements Parcelable {
        String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                context.getContentResolver()) : null;
        try {
            intent.migrateExtraStreamToClipData();
            intent.migrateExtraStreamToClipData(context);
            intent.prepareToLeaveProcess(context);
            IIntentSender target =
                ActivityManager.getService().getIntentSenderWithFeature(
@@ -491,7 +491,7 @@ public final class PendingIntent implements Parcelable {
        String packageName = context.getPackageName();
        String[] resolvedTypes = new String[intents.length];
        for (int i=0; i<intents.length; i++) {
            intents[i].migrateExtraStreamToClipData();
            intents[i].migrateExtraStreamToClipData(context);
            intents[i].prepareToLeaveProcess(context);
            resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
        }
@@ -517,7 +517,7 @@ public final class PendingIntent implements Parcelable {
        String packageName = context.getPackageName();
        String[] resolvedTypes = new String[intents.length];
        for (int i=0; i<intents.length; i++) {
            intents[i].migrateExtraStreamToClipData();
            intents[i].migrateExtraStreamToClipData(context);
            intents[i].prepareToLeaveProcess(context);
            resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
        }
+37 −3
Original line number Diff line number Diff line
@@ -11272,6 +11272,19 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    public boolean migrateExtraStreamToClipData() {
        return migrateExtraStreamToClipData(AppGlobals.getInitialApplication());
    }

    /**
     * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
     * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
     * intents in {@link #ACTION_CHOOSER}.
     *
     * @param context app context
     * @return Whether any contents were migrated.
     * @hide
     */
    public boolean migrateExtraStreamToClipData(Context context) {
        // Refuse to touch if extras already parcelled
        if (mExtras != null && mExtras.isParcelled()) return false;

@@ -11289,7 +11302,7 @@ public class Intent implements Parcelable, Cloneable {
            try {
                final Intent intent = getParcelableExtra(EXTRA_INTENT);
                if (intent != null) {
                    migrated |= intent.migrateExtraStreamToClipData();
                    migrated |= intent.migrateExtraStreamToClipData(context);
                }
            } catch (ClassCastException e) {
            }
@@ -11299,7 +11312,7 @@ public class Intent implements Parcelable, Cloneable {
                    for (int i = 0; i < intents.length; i++) {
                        final Intent intent = (Intent) intents[i];
                        if (intent != null) {
                            migrated |= intent.migrateExtraStreamToClipData();
                            migrated |= intent.migrateExtraStreamToClipData(context);
                        }
                    }
                }
@@ -11362,13 +11375,17 @@ public class Intent implements Parcelable, Cloneable {
            } catch (ClassCastException e) {
            }
        } else if (isImageCaptureIntent()) {
            final Uri output;
            Uri output;
            try {
                output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
            } catch (ClassCastException e) {
                return false;
            }

            if (output != null) {
                output = maybeConvertFileToContentUri(context, output);
                putExtra(MediaStore.EXTRA_OUTPUT, output);

                setClipData(ClipData.newRawUri("", output));
                addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
                return true;
@@ -11378,6 +11395,23 @@ public class Intent implements Parcelable, Cloneable {
        return false;
    }

    private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
                && context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.R) {
            File file = new File(uri.getPath());
            try {
                if (!file.exists()) file.createNewFile();
                uri = MediaStore.scanFile(context.getContentResolver(), new File(uri.getPath()));
                if (uri != null) {
                    return uri;
                }
            } catch (IOException e) {
                Log.e(TAG, "Ignoring failure to create file " + file, e);
            }
        }
        return uri;
    }

    /**
     * Convert the dock state to a human readable format.
     * @hide
Loading