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.
+ }
+ }
}