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;