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

Commit c14df8e7 authored by Vladislav Kaznacheev's avatar Vladislav Kaznacheev
Browse files

Fix SecurityException in Editor.onDrop

When a content URI is dropped onto EditText, it tries making sense
of the contents, and in the process it accesses the content provider.
If this content provider requires a permission grant, SecurityException
occurs.

This fix does two things:
1. Editor.onDrop now requests DropPermissions and releases is
when it is done. This required the introduction of a new hidden method
DropPermissions.takeTransient, because the existing method required
access to an Activity instance.

2. If the drag originator neglected to allow the permission grant,
DropPermission request would not help, so a try/catch block
is added to Editor.onDrop to avoid breaking the recipient app.

Bug: 26694948
Change-Id: I714429a507e62c83a150d91fbcdee791bced3ad3
parent 92b26c5b
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -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 @@ public class ClipData implements Parcelable {
                // 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) {
+21 −0
Original line number Diff line number Diff line
@@ -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 @@ public final class DropPermissions {

    private final IDropPermissions mDropPermissions;

    private IBinder mPermissionOwnerToken;

    private final CloseGuard mCloseGuard = CloseGuard.get();

    /**
@@ -79,12 +82,30 @@ public final class DropPermissions {
        return true;
    }

    /**
     * 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();
+19 −5
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import android.view.ActionMode.Callback;
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;
@@ -2292,12 +2293,25 @@ public class Editor {

    void onDrop(DragEvent event) {
        StringBuilder content = new StringBuilder("");

        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());

+1 −0
Original line number Diff line number Diff line
@@ -24,5 +24,6 @@ import android.os.IBinder;
 */
interface IDropPermissions {
    void take(IBinder activityToken);
    void takeTransient(IBinder permissionOwnerToken);
    void release();
}
+42 −11
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ import com.android.internal.view.IDropPermissions;

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 @@ class DropPermissionsHandler extends IDropPermissions.Stub {
    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 @@ class DropPermissionsHandler extends IDropPermissions.Stub {

    @Override
    public void take(IBinder activityToken) throws RemoteException {
        if (mActivityToken != null) {
        if (mActivityToken != null || mPermissionOwnerToken != null) {
            return;
        }
        mActivityToken = activityToken;
@@ -61,6 +62,10 @@ class DropPermissionsHandler extends IDropPermissions.Stub {
        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++) {
@@ -73,13 +78,25 @@ class DropPermissionsHandler extends IDropPermissions.Stub {
        }
    }

    @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;
        if (mActivityToken != null) {
            try {
                permissionOwner = ActivityManagerNative.getDefault().
                        getUriPermissionOwnerForActivity(mActivityToken);
@@ -89,10 +106,24 @@ class DropPermissionsHandler extends IDropPermissions.Stub {
            } finally {
                mActivityToken = null;
            }
        } else {
            permissionOwner = mPermissionOwnerToken;
            mPermissionOwnerToken.unlinkToDeath(this, 0);
            mPermissionOwnerToken = null;
        }

        for (int i = 0; i < mUris.size(); ++i) {
            ActivityManagerNative.getDefault().revokeUriPermissionFromOwner(
                    permissionOwner, mUris.get(i), mMode, mSourceUserId);
        }
    }

    @Override
    public void binderDied() {
        try {
            release();
        } catch (RemoteException e) {
            // Cannot happen, local call.
        }
    }
}