Merge "Fix SecurityException in Editor.onDrop"
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 0b80f8a..6dfefac 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -17,11 +17,14 @@
 package android.content;
 
 import static android.content.ContentProvider.maybeAddUserId;
+
+import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.StrictMode;
 import android.text.Html;
 import android.text.Spannable;
@@ -462,7 +465,12 @@
                 // Check to see what data representations the content
                 // provider supports.  We would like HTML text, but if that
                 // is not possible we'll live with plan text.
-                String[] types = context.getContentResolver().getStreamTypes(mUri, "text/*");
+                String[] types = null;
+                try {
+                    types = context.getContentResolver().getStreamTypes(mUri, "text/*");
+                } catch (SecurityException e) {
+                    // No read permission for mUri, assume empty stream types list.
+                }
                 boolean hasHtml = false;
                 boolean hasText = false;
                 if (types != null) {
diff --git a/core/java/android/view/DropPermissions.java b/core/java/android/view/DropPermissions.java
index 8c948a9..5411dad 100644
--- a/core/java/android/view/DropPermissions.java
+++ b/core/java/android/view/DropPermissions.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.app.ActivityManagerNative;
 import android.os.IBinder;
 import android.os.RemoteException;
 import com.android.internal.view.IDropPermissions;
@@ -41,6 +42,8 @@
 
     private final IDropPermissions mDropPermissions;
 
+    private IBinder mPermissionOwnerToken;
+
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     /**
@@ -80,11 +83,29 @@
     }
 
     /**
+     * Take the permissions. Must call {@link #release} explicitly.
+     * @return True if permissions are successfully taken.
+     * @hide
+     */
+    public boolean takeTransient() {
+        try {
+            mPermissionOwnerToken = ActivityManagerNative.getDefault().
+                    newUriPermissionOwner("drop");
+            mDropPermissions.takeTransient(mPermissionOwnerToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+        mCloseGuard.open("release");
+        return true;
+    }
+
+    /**
      * Revoke permissions explicitly.
      */
     public void release() {
         try {
             mDropPermissions.release();
+            mPermissionOwnerToken = null;
         } catch (RemoteException e) {
         }
         mCloseGuard.close();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 67473c6..617d3dd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -77,6 +77,7 @@
 import android.view.ContextMenu;
 import android.view.DisplayListCanvas;
 import android.view.DragEvent;
+import android.view.DropPermissions;
 import android.view.Gravity;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
@@ -2298,11 +2299,24 @@
 
     void onDrop(DragEvent event) {
         StringBuilder content = new StringBuilder("");
-        ClipData clipData = event.getClipData();
-        final int itemCount = clipData.getItemCount();
-        for (int i=0; i < itemCount; i++) {
-            Item item = clipData.getItemAt(i);
-            content.append(item.coerceToStyledText(mTextView.getContext()));
+
+        final DropPermissions dropPermissions = DropPermissions.obtain(event);
+        if (dropPermissions != null) {
+            dropPermissions.takeTransient();
+        }
+
+        try {
+            ClipData clipData = event.getClipData();
+            final int itemCount = clipData.getItemCount();
+            for (int i=0; i < itemCount; i++) {
+                Item item = clipData.getItemAt(i);
+                content.append(item.coerceToStyledText(mTextView.getContext()));
+            }
+        }
+        finally {
+            if (dropPermissions != null) {
+                dropPermissions.release();
+            }
         }
 
         final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
diff --git a/core/java/com/android/internal/view/IDropPermissions.aidl b/core/java/com/android/internal/view/IDropPermissions.aidl
index 2438bda..74ff4b4 100644
--- a/core/java/com/android/internal/view/IDropPermissions.aidl
+++ b/core/java/com/android/internal/view/IDropPermissions.aidl
@@ -24,5 +24,6 @@
  */
 interface IDropPermissions {
     void take(IBinder activityToken);
+    void takeTransient(IBinder permissionOwnerToken);
     void release();
 }
diff --git a/services/core/java/com/android/server/wm/DropPermissionsHandler.java b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
index 68cfaab..5889fb8 100644
--- a/services/core/java/com/android/server/wm/DropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DropPermissionsHandler.java
@@ -27,7 +27,7 @@
 
 import java.util.ArrayList;
 
-class DropPermissionsHandler extends IDropPermissions.Stub {
+class DropPermissionsHandler extends IDropPermissions.Stub implements IBinder.DeathRecipient {
 
     private final int mSourceUid;
     private final String mTargetPackage;
@@ -38,6 +38,7 @@
     private final ArrayList<Uri> mUris = new ArrayList<Uri>();
 
     private IBinder mActivityToken = null;
+    private IBinder mPermissionOwnerToken = null;
 
     DropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode,
             int sourceUserId, int targetUserId) {
@@ -52,7 +53,7 @@
 
     @Override
     public void take(IBinder activityToken) throws RemoteException {
-        if (mActivityToken != null) {
+        if (mActivityToken != null || mPermissionOwnerToken != null) {
             return;
         }
         mActivityToken = activityToken;
@@ -61,6 +62,10 @@
         IBinder permissionOwner = ActivityManagerNative.getDefault().
                 getUriPermissionOwnerForActivity(mActivityToken);
 
+        doTake(permissionOwner);
+    }
+
+    private void doTake(IBinder permissionOwner) throws RemoteException {
         long origId = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < mUris.size(); i++) {
@@ -74,20 +79,37 @@
     }
 
     @Override
+    public void takeTransient(IBinder permissionOwnerToken) throws RemoteException {
+        if (mActivityToken != null || mPermissionOwnerToken != null) {
+            return;
+        }
+        mPermissionOwnerToken = permissionOwnerToken;
+        mPermissionOwnerToken.linkToDeath(this, 0);
+
+        doTake(mPermissionOwnerToken);
+    }
+
+    @Override
     public void release() throws RemoteException {
-        if (mActivityToken == null) {
+        if (mActivityToken == null && mPermissionOwnerToken == null) {
             return;
         }
 
         IBinder permissionOwner = null;
-        try {
-            permissionOwner = ActivityManagerNative.getDefault().
-                    getUriPermissionOwnerForActivity(mActivityToken);
-        } catch (Exception e) {
-            // Activity is destroyed, permissions already revoked.
-            return;
-        } finally {
-            mActivityToken = null;
+        if (mActivityToken != null) {
+            try {
+                permissionOwner = ActivityManagerNative.getDefault().
+                        getUriPermissionOwnerForActivity(mActivityToken);
+            } catch (Exception e) {
+                // Activity is destroyed, permissions already revoked.
+                return;
+            } finally {
+                mActivityToken = null;
+            }
+        } else {
+            permissionOwner = mPermissionOwnerToken;
+            mPermissionOwnerToken.unlinkToDeath(this, 0);
+            mPermissionOwnerToken = null;
         }
 
         for (int i = 0; i < mUris.size(); ++i) {
@@ -95,4 +117,13 @@
                     permissionOwner, mUris.get(i), mMode, mSourceUserId);
         }
     }
+
+    @Override
+    public void binderDied() {
+        try {
+            release();
+        } catch (RemoteException e) {
+            // Cannot happen, local call.
+        }
+    }
 }