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
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 287ea35..ae60476 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -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.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 @@
private Rect mSourceBounds;
private Intent mSelector;
private ClipData mClipData;
+ private int mContentUserHint = UserHandle.USER_CURRENT;
// ---------------------------------------------------------------------
@@ -3956,6 +3959,7 @@
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 @@
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.
@@ -5679,6 +5688,16 @@
}
/**
+ * 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
* like "com.android.contacts.ShowAll".
@@ -6731,6 +6750,7 @@
@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 @@
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 @@
&& (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 @@
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 @@
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 @@
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 @@
} else {
out.writeInt(0);
}
-
+ out.writeInt(mContentUserHint);
out.writeBundle(mExtras);
}
@@ -7257,7 +7292,7 @@
if (in.readInt() != 0) {
mClipData = new ClipData(in);
}
-
+ mContentUserHint = in.readInt();
mExtras = in.readBundle();
}
@@ -7467,39 +7502,50 @@
}
/**
- * 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));
+ }
}
- }
+ }
/**
* Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
@@ -7590,6 +7636,20 @@
}
} 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;