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

Commit 0f8ee743 authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Implement permission granting in clipboard."

parents ecba4f33 90f4aafa
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -1341,6 +1341,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
        }

        case CHECK_GRANT_URI_PERMISSION_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            int callingUid = data.readInt();
            String targetPkg = data.readString();
            Uri uri = Uri.CREATOR.createFromParcel(data);
            int modeFlags = data.readInt();
            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags);
            reply.writeNoException();
            reply.writeInt(res);
            return true;
        }

        case DUMP_HEAP_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            String process = data.readString();
@@ -2998,6 +3010,23 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
    }

    public int checkGrantUriPermission(int callingUid, String targetPkg,
            Uri uri, int modeFlags) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeInt(callingUid);
        data.writeString(targetPkg);
        uri.writeToParcel(data, 0);
        data.writeInt(modeFlags);
        mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        data.recycle();
        reply.recycle();
        return res;
    }

    public boolean dumpHeap(String process, boolean managed,
            String path, ParcelFileDescriptor fd) throws RemoteException {
        Parcel data = Parcel.obtain();
+5 −1
Original line number Diff line number Diff line
@@ -326,6 +326,9 @@ public interface IActivityManager extends IInterface {
    public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
            int mode) throws RemoteException;

    public int checkGrantUriPermission(int callingUid, String targetPkg,
            Uri uri, int modeFlags) throws RemoteException;

    // Cause the specified process to dump the specified heap.
    public boolean dumpHeap(String process, boolean managed, String path,
        ParcelFileDescriptor fd) throws RemoteException;
@@ -540,5 +543,6 @@ public interface IActivityManager extends IInterface {
    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
    int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
}
+1 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
     */
    public ClipData getPrimaryClip() {
        try {
            return getService().getPrimaryClip();
            return getService().getPrimaryClip(mContext.getPackageName());
        } catch (RemoteException e) {
            return null;
        }
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ import android.content.IOnPrimaryClipChangedListener;
 */
interface IClipboard {
    void setPrimaryClip(in ClipData clip);
    ClipData getPrimaryClip();
    ClipData getPrimaryClip(String pkg);
    ClipDescription getPrimaryClipDescription();
    boolean hasPrimaryClip();
    void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
+159 −3
Original line number Diff line number Diff line
@@ -16,32 +16,81 @@

package com.android.server;

import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Pair;
import android.util.Slog;

import java.util.HashSet;

/**
 * Implementation of the clipboard for copy and paste.
 */
public class ClipboardService extends IClipboard.Stub {
    private ClipData mPrimaryClip;
    private final Context mContext;
    private final IActivityManager mAm;
    private final PackageManager mPm;
    private final IBinder mPermissionOwner;

    private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
            = new RemoteCallbackList<IOnPrimaryClipChangedListener>();

    private ClipData mPrimaryClip;

    private final HashSet<String> mActivePermissionOwners
            = new HashSet<String>();

    /**
     * Instantiates the clipboard.
     */
    public ClipboardService(Context context) { }
    public ClipboardService(Context context) {
        mContext = context;
        mAm = ActivityManagerNative.getDefault();
        mPm = context.getPackageManager();
        IBinder permOwner = null;
        try {
            permOwner = mAm.newUriPermissionOwner("clipboard");
        } catch (RemoteException e) {
            Slog.w("clipboard", "AM dead", e);
        }
        mPermissionOwner = permOwner;
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            Slog.w("clipboard", "Exception: ", e);
            throw e;
        }
        
    }

    public void setPrimaryClip(ClipData clip) {
        synchronized (this) {
            if (clip != null && clip.getItemCount() <= 0) {
                throw new IllegalArgumentException("No items");
            }
            checkDataOwnerLocked(clip, Binder.getCallingUid());
            clearActiveOwnersLocked();
            mPrimaryClip = clip;
            final int n = mPrimaryClipListeners.beginBroadcast();
            for (int i = 0; i < n; i++) {
@@ -57,8 +106,9 @@ public class ClipboardService extends IClipboard.Stub {
        }
    }
    
    public ClipData getPrimaryClip() {
    public ClipData getPrimaryClip(String pkg) {
        synchronized (this) {
            addActiveOwnerLocked(Binder.getCallingUid(), pkg);
            return mPrimaryClip;
        }
    }
@@ -96,4 +146,110 @@ public class ClipboardService extends IClipboard.Stub {
            return false;
        }
    }

    private final void checkUriOwnerLocked(Uri uri, int uid) {
        if (!"content".equals(uri.getScheme())) {
            return;
        }
        long ident = Binder.clearCallingIdentity();
        boolean allowed = false;
        try {
            // This will throw SecurityException for us.
            mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } catch (RemoteException e) {
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private final void checkItemOwnerLocked(ClipData.Item item, int uid) {
        if (item.getUri() != null) {
            checkUriOwnerLocked(item.getUri(), uid);
        }
        Intent intent = item.getIntent();
        if (intent != null && intent.getData() != null) {
            checkUriOwnerLocked(intent.getData(), uid);
        }
    }

    private final void checkDataOwnerLocked(ClipData data, int uid) {
        final int N = data.getItemCount();
        for (int i=0; i<N; i++) {
            checkItemOwnerLocked(data.getItem(i), uid);
        }
    }

    private final void grantUriLocked(Uri uri, String pkg) {
        long ident = Binder.clearCallingIdentity();
        try {
            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } catch (RemoteException e) {
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private final void grantItemLocked(ClipData.Item item, String pkg) {
        if (item.getUri() != null) {
            grantUriLocked(item.getUri(), pkg);
        }
        Intent intent = item.getIntent();
        if (intent != null && intent.getData() != null) {
            grantUriLocked(intent.getData(), pkg);
        }
    }

    private final void addActiveOwnerLocked(int uid, String pkg) {
        PackageInfo pi;
        try {
            pi = mPm.getPackageInfo(pkg, 0);
            if (pi.applicationInfo.uid != uid) {
                throw new SecurityException("Calling uid " + uid
                        + " does not own package " + pkg);
            }
        } catch (NameNotFoundException e) {
            throw new IllegalArgumentException("Unknown package " + pkg, e);
        }
        if (!mActivePermissionOwners.contains(pkg)) {
            final int N = mPrimaryClip.getItemCount();
            for (int i=0; i<N; i++) {
                grantItemLocked(mPrimaryClip.getItem(i), pkg);
            }
            mActivePermissionOwners.add(pkg);
        }
    }

    private final void revokeUriLocked(Uri uri) {
        long ident = Binder.clearCallingIdentity();
        try {
            mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        } catch (RemoteException e) {
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private final void revokeItemLocked(ClipData.Item item) {
        if (item.getUri() != null) {
            revokeUriLocked(item.getUri());
        }
        Intent intent = item.getIntent();
        if (intent != null && intent.getData() != null) {
            revokeUriLocked(intent.getData());
        }
    }

    private final void clearActiveOwnersLocked() {
        mActivePermissionOwners.clear();
        if (mPrimaryClip == null) {
            return;
        }
        final int N = mPrimaryClip.getItemCount();
        for (int i=0; i<N; i++) {
            revokeItemLocked(mPrimaryClip.getItem(i));
        }
    }
}
Loading