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

Commit d1c99b1f authored by Nicolas Prevot's avatar Nicolas Prevot
Browse files

Migrate extras to ClipData for image/video capture intents.

The intents ACTION_IMAGE_CAPTURE, ACTION_IMAGE_CAPTURE_SECURE and ACTION_VIDEO_CAPTURE are now handled in a way
similar to ACTION_SEND and ACTION_SEND_MULTIPLE.
Migrate the uri in the EXTRA_OUTPUT extra to clipData, and add the flag GRANT_WRITE_URI_PERMISSION.

The userIds are now added to extra uris in the process receiving the intent, (not in the system process), because the
system process may not be able to parcel/unparcel the extras.

BUG: 15534203

Change-Id: I8f79666b726bc6d7745bf777ad3c7518945c5cc3
parent a65118e1
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -2205,6 +2205,7 @@ public final class ActivityThread {
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
@@ -2429,6 +2430,7 @@ public final class ActivityThread {
        for (int i=0; i<N; i++) {
            Intent intent = intents.get(i);
            intent.setExtrasClassLoader(r.activity.getClassLoader());
            intent.prepareToEnterProcess();
            r.activity.mFragments.noteStateNotSaved();
            mInstrumentation.callActivityOnNewIntent(r.activity, intent);
        }
@@ -2552,6 +2554,7 @@ public final class ActivityThread {
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch (Exception e) {
@@ -2753,6 +2756,7 @@ public final class ActivityThread {
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
@@ -2781,6 +2785,7 @@ public final class ActivityThread {
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                boolean doRebind = s.onUnbind(data.intent);
                try {
                    if (doRebind) {
@@ -2856,6 +2861,7 @@ public final class ActivityThread {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
                }
                int res;
                if (!data.taskRemoved) {
@@ -3506,6 +3512,7 @@ public final class ActivityThread {
            try {
                if (ri.mData != null) {
                    ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
                    ri.mData.prepareToEnterProcess();
                }
                if (DEBUG_RESULTS) Slog.v(TAG,
                        "Delivering result to activity " + r + " : " + ri);
+4 −8
Original line number Diff line number Diff line
@@ -810,20 +810,16 @@ public class ClipData implements Parcelable {
        }
    }

    /**
     * Prepare this {@link ClipData} to leave an app process.
     *
     * @hide
     */
    public void prepareToLeaveUser(int userId) {
    /** @hide */
    public void fixUris(int contentUserHint) {
        final int size = mItems.size();
        for (int i = 0; i < size; i++) {
            final Item item = mItems.get(i);
            if (item.mIntent != null) {
                item.mIntent.prepareToLeaveUser(userId);
                item.mIntent.fixUris(contentUserHint);
            }
            if (item.mUri != null) {
                item.mUri = maybeAddUserId(item.mUri, userId);
                item.mUri = maybeAddUserId(item.mUri, contentUserHint);
            }
        }
    }
+76 −16
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.content;

import android.content.pm.ApplicationInfo;
import android.provider.MediaStore;
import android.util.ArraySet;

import org.xmlpull.v1.XmlPullParser;
@@ -38,6 +39,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
import android.os.UserHandle;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.OpenableColumns;
@@ -3937,6 +3939,7 @@ public class Intent implements Parcelable, Cloneable {
    private Rect mSourceBounds;
    private Intent mSelector;
    private ClipData mClipData;
    private int mContentUserHint = UserHandle.USER_CURRENT;

    // ---------------------------------------------------------------------

@@ -3956,6 +3959,7 @@ public class Intent implements Parcelable, Cloneable {
        this.mPackage = o.mPackage;
        this.mComponent = o.mComponent;
        this.mFlags = o.mFlags;
        this.mContentUserHint = o.mContentUserHint;
        if (o.mCategories != null) {
            this.mCategories = new ArraySet<String>(o.mCategories);
        }
@@ -4660,6 +4664,11 @@ public class Intent implements Parcelable, Cloneable {
        return mClipData;
    }

    /** @hide */
    public int getContentUserHint() {
        return mContentUserHint;
    }

    /**
     * Sets the ClassLoader that will be used when unmarshalling
     * any Parcelable values from the extras of this Intent.
@@ -5678,6 +5687,16 @@ public class Intent implements Parcelable, Cloneable {
        mClipData = clip;
    }

    /**
     * This is NOT a secure mechanism to identify the user who sent the intent.
     * When the intent is sent to a different user, it is used to fix uris by adding the userId
     * who sent the intent.
     * @hide
     */
    public void setContentUserHint(int contentUserHint) {
        mContentUserHint = contentUserHint;
    }

    /**
     * Add extended data to the intent.  The name must include a package
     * prefix, for example the app com.android.contacts would use names
@@ -6731,6 +6750,7 @@ public class Intent implements Parcelable, Cloneable {
    @FillInFlags
    public int fillIn(Intent other, @FillInFlags int flags) {
        int changes = 0;
        boolean mayHaveCopiedUris = false;
        if (other.mAction != null
                && (mAction == null || (flags&FILL_IN_ACTION) != 0)) {
            mAction = other.mAction;
@@ -6742,6 +6762,7 @@ public class Intent implements Parcelable, Cloneable {
            mData = other.mData;
            mType = other.mType;
            changes |= FILL_IN_DATA;
            mayHaveCopiedUris = true;
        }
        if (other.mCategories != null
                && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) {
@@ -6771,6 +6792,7 @@ public class Intent implements Parcelable, Cloneable {
                && (mClipData == null || (flags&FILL_IN_CLIP_DATA) != 0)) {
            mClipData = other.mClipData;
            changes |= FILL_IN_CLIP_DATA;
            mayHaveCopiedUris = true;
        }
        // Component is special: it can -only- be set if explicitly allowed,
        // since otherwise the sender could force the intent somewhere the
@@ -6788,12 +6810,14 @@ public class Intent implements Parcelable, Cloneable {
        if (mExtras == null) {
            if (other.mExtras != null) {
                mExtras = new Bundle(other.mExtras);
                mayHaveCopiedUris = true;
            }
        } else if (other.mExtras != null) {
            try {
                Bundle newb = new Bundle(other.mExtras);
                newb.putAll(mExtras);
                mExtras = newb;
                mayHaveCopiedUris = true;
            } catch (RuntimeException e) {
                // Modifying the extras can cause us to unparcel the contents
                // of the bundle, and if we do this in the system process that
@@ -6803,6 +6827,10 @@ public class Intent implements Parcelable, Cloneable {
                Log.w("Intent", "Failure filling in extras", e);
            }
        }
        if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
                && other.mContentUserHint != UserHandle.USER_CURRENT) {
            mContentUserHint = other.mContentUserHint;
        }
        return changes;
    }

@@ -7030,8 +7058,15 @@ public class Intent implements Parcelable, Cloneable {
            first = false;
            b.append("(has extras)");
        }
        if (mContentUserHint != UserHandle.USER_CURRENT) {
            if (!first) {
                b.append(' ');
            }
            first = false;
            b.append("u=").append(mContentUserHint);
        }
        if (mSelector != null) {
            b.append(" sel={");
            b.append(" sel=");
            mSelector.toShortString(b, secure, comp, extras, clip);
            b.append("}");
        }
@@ -7208,7 +7243,7 @@ public class Intent implements Parcelable, Cloneable {
        } else {
            out.writeInt(0);
        }

        out.writeInt(mContentUserHint);
        out.writeBundle(mExtras);
    }

@@ -7257,7 +7292,7 @@ public class Intent implements Parcelable, Cloneable {
        if (in.readInt() != 0) {
            mClipData = new ClipData(in);
        }

        mContentUserHint = in.readInt();
        mExtras = in.readBundle();
    }

@@ -7467,37 +7502,48 @@ public class Intent implements Parcelable, Cloneable {
    }

    /**
     * Prepare this {@link Intent} to be sent to another user
     *
     * @hide
     */
    public void prepareToLeaveUser(int userId) {
    public void prepareToEnterProcess() {
        if (mContentUserHint != UserHandle.USER_CURRENT) {
            fixUris(mContentUserHint);
            mContentUserHint = UserHandle.USER_CURRENT;
        }
    }

    /**
     * @hide
     */
     public void fixUris(int contentUserHint) {
        Uri data = getData();
        if (data != null) {
            mData = maybeAddUserId(data, userId);
        }
        if (mSelector != null) {
            mSelector.prepareToLeaveUser(userId);
            mData = maybeAddUserId(data, contentUserHint);
        }
        if (mClipData != null) {
            mClipData.prepareToLeaveUser(userId);
            mClipData.fixUris(contentUserHint);
        }
        String action = getAction();
        if (ACTION_SEND.equals(action)) {
            final Uri stream = getParcelableExtra(EXTRA_STREAM);
            if (stream != null) {
                putExtra(EXTRA_STREAM, maybeAddUserId(stream, userId));
            }
                putExtra(EXTRA_STREAM, maybeAddUserId(stream, contentUserHint));
            }
        if (ACTION_SEND_MULTIPLE.equals(action)) {
        } else if (ACTION_SEND_MULTIPLE.equals(action)) {
            final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
            if (streams != null) {
                ArrayList<Uri> newStreams = new ArrayList<Uri>();
                for (int i = 0; i < streams.size(); i++) {
                    newStreams.add(maybeAddUserId(streams.get(i), userId));
                    newStreams.add(maybeAddUserId(streams.get(i), contentUserHint));
                }
                putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
            }
        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
            final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
            if (output != null) {
                putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
            }
        }
     }

@@ -7590,6 +7636,20 @@ public class Intent implements Parcelable, Cloneable {
                }
            } catch (ClassCastException e) {
            }
        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
            final Uri output;
            try {
                output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
            } catch (ClassCastException e) {
                return false;
            }
            if (output != null) {
                setClipData(ClipData.newRawUri("", output));
                addFlags(FLAG_GRANT_WRITE_URI_PERMISSION|FLAG_GRANT_READ_URI_PERMISSION);
                return true;
            }
        }

        return false;
+15 −0
Original line number Diff line number Diff line
@@ -256,6 +256,11 @@ public final class MediaStore {
     * object in the extra field. This is useful for applications that only need a small image.
     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
     * value of EXTRA_OUTPUT.
     * As of {@link android.os.Build.VERSION_CODES#L}, this uri can also be supplied through
     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
     * If you don't set a ClipData, it will be copied there for you when calling
     * {@link Context#startActivity(Intent)}.
     * @see #EXTRA_OUTPUT
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -276,6 +281,11 @@ public final class MediaStore {
     * object in the extra field. This is useful for applications that only need a small image.
     * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
     * value of EXTRA_OUTPUT.
     * As of {@link android.os.Build.VERSION_CODES#L}, this uri can also be supplied through
     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
     * If you don't set a ClipData, it will be copied there for you when calling
     * {@link Context#startActivity(Intent)}.
     *
     * @see #ACTION_IMAGE_CAPTURE
     * @see #EXTRA_OUTPUT
@@ -294,6 +304,11 @@ public final class MediaStore {
     * where the video is written. If EXTRA_OUTPUT is not present the video will be
     * written to the standard location for videos, and the Uri of that location will be
     * returned in the data field of the Uri.
     * As of {@link android.os.Build.VERSION_CODES#L}, this uri can also be supplied through
     * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
     * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
     * If you don't set a ClipData, it will be copied there for you when calling
     * {@link Context#startActivity(Intent)}.
     * @see #EXTRA_OUTPUT
     * @see #EXTRA_VIDEO_QUALITY
     * @see #EXTRA_SIZE_LIMIT
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ public class IntentForwarderActivity extends Activity {
            Slog.e(TAG, "PackageManagerService is dead?");
        }
        if (canForward) {
            newIntent.prepareToLeaveUser(callingUserId);
            newIntent.setContentUserHint(callingUserId);

            final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser(
                        newIntent, MATCH_DEFAULT_ONLY, userDest.getIdentifier());
Loading