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

Commit 90f4aafa authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Implement permission granting in clipboard.

Change-Id: I9a7a949d1aaf4b3beabceaf807fb7d3b040e4ea8
parent e2163557
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