Merge "Making the clipboard work across users." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index e0006f6..5d9d337 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22731,6 +22731,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
     field public static final java.lang.String DISALLOW_CREATE_WINDOWS = "no_create_windows";
+    field public static final java.lang.String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
     field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
     field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 342155d..318a520 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1661,8 +1661,10 @@
             String targetPkg = data.readString();
             Uri uri = Uri.CREATOR.createFromParcel(data);
             int mode = data.readInt();
-            int userId = data.readInt();
-            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, userId);
+            int sourceUserId = data.readInt();
+            int targetUserId = data.readInt();
+            grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, sourceUserId,
+                    targetUserId);
             reply.writeNoException();
             return true;
         }
@@ -4343,7 +4345,7 @@
     }
 
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode, int userId) throws RemoteException {
+            Uri uri, int mode, int sourceUserId, int targetUserId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -4352,7 +4354,8 @@
         data.writeString(targetPkg);
         uri.writeToParcel(data, 0);
         data.writeInt(mode);
-        data.writeInt(userId);
+        data.writeInt(sourceUserId);
+        data.writeInt(targetUserId);
         mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cc13a3b..53c1408 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -332,7 +332,7 @@
 
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
-            Uri uri, int mode, int userId) throws RemoteException;
+            Uri uri, int mode, int sourceUserId, int targetUserId) throws RemoteException;
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
             int mode, int userId) throws RemoteException;
 
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index b44abf9..7a16ef8 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -828,6 +828,26 @@
         }
     }
 
+    /**
+     * Only fixing the data field of the intents
+     * @hide
+     */
+    public void fixUrisLight(int contentUserHint) {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                Uri data = item.mIntent.getData();
+                if (data != null) {
+                    item.mIntent.setData(maybeAddUserId(data, contentUserHint));
+                }
+            }
+            if (item.mUri != null) {
+                item.mUri = maybeAddUserId(item.mUri, contentUserHint);
+            }
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d3aee50..45edf28 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -310,6 +310,18 @@
      */
     public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
 
+    /**
+     * Key for user restrictions. Specifies if what is copied in the clipboard of this profile can
+     * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
+     * pasted in this profile.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CROSS_PROFILE_COPY_PASTE = "no_cross_profile_copy_paste";
+
     /** @hide */
     public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
     /** @hide */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ab8f2f5..c0ac023 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6563,13 +6563,20 @@
     }
 
     void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, UriPermissionOwner owner) {
+            final int modeFlags, UriPermissionOwner owner, int targetUserId) {
         if (targetPkg == null) {
             throw new NullPointerException("targetPkg");
         }
+        int targetUid;
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            targetUid = pm.getPackageUid(targetPkg, targetUserId);
+        } catch (RemoteException ex) {
+            return;
+        }
 
-        int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
-                -1);
+        targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
+                targetUid);
         if (targetUid < 0) {
             return;
         }
@@ -6720,7 +6727,8 @@
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
 
-            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null);
+            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null,
+                    UserHandle.getUserId(r.uid));
         }
     }
 
@@ -6897,7 +6905,7 @@
 
     @Override
     public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
-            final int modeFlags, int userId) {
+            final int modeFlags, int sourceUserId, int targetUserId) {
         synchronized(this) {
             UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
             if (owner == null) {
@@ -6917,8 +6925,8 @@
                 throw new IllegalArgumentException("null uri");
             }
 
-            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(userId, uri, false),
-                    modeFlags, owner);
+            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
+                    modeFlags, owner, targetUserId);
         }
     }
 
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 15e3e89..1c26846 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -32,18 +32,23 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.IUserManager;
 import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Implementation of the clipboard for copy and paste.
@@ -54,6 +59,7 @@
 
     private final Context mContext;
     private final IActivityManager mAm;
+    private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
     private final IBinder mPermissionOwner;
@@ -92,6 +98,7 @@
         mContext = context;
         mAm = ActivityManagerNative.getDefault();
         mPm = context.getPackageManager();
+        mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
         IBinder permOwner = null;
         try {
@@ -161,32 +168,81 @@
                 return;
             }
             checkDataOwnerLocked(clip, callingUid);
-            clearActiveOwnersLocked();
-            PerUserClipboard clipboard = getClipboard();
-            clipboard.primaryClip = clip;
-            final long ident = Binder.clearCallingIdentity();
-            final int n = clipboard.primaryClipListeners.beginBroadcast();
-            try {
-                for (int i = 0; i < n; i++) {
+            final int userId = UserHandle.getUserId(callingUid);
+            PerUserClipboard clipboard = getClipboard(userId);
+            revokeUris(clipboard);
+            setPrimaryClipInternal(clipboard, clip);
+            List<UserInfo> related = getRelatedProfiles(userId);
+            if (related != null) {
+                int size = related.size();
+                if (size > 1) { // Related profiles list include the current profile.
+                    boolean canCopy = false;
                     try {
-                        ListenerInfo li = (ListenerInfo)
-                                clipboard.primaryClipListeners.getBroadcastCookie(i);
-                        if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid,
-                                li.mPackageName) == AppOpsManager.MODE_ALLOWED) {
-                            clipboard.primaryClipListeners.getBroadcastItem(i)
-                                    .dispatchPrimaryClipChanged();
-                        }
+                        canCopy = !mUm.getUserRestrictions(userId).getBoolean(
+                                UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
                     } catch (RemoteException e) {
-                        // The RemoteCallbackList will take care of removing
-                        // the dead object for us.
+                        Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+                    }
+                    // Copy clip data to related users if allowed. If disallowed, then remove
+                    // primary clip in related users to prevent pasting stale content.
+                    if (!canCopy) {
+                        clip = null;
+                    } else {
+                        clip.fixUrisLight(userId);
+                    }
+                    for (int i = 0; i < size; i++) {
+                        int id = related.get(i).id;
+                        if (id != userId) {
+                            setPrimaryClipInternal(getClipboard(id), clip);
+                        }
                     }
                 }
-            } finally {
-                clipboard.primaryClipListeners.finishBroadcast();
-                Binder.restoreCallingIdentity(ident);
             }
         }
     }
+
+    List<UserInfo> getRelatedProfiles(int userId) {
+        final List<UserInfo> related;
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            related = mUm.getProfiles(userId, true);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+            return null;
+        } finally{
+            Binder.restoreCallingIdentity(origId);
+        }
+        return related;
+    }
+
+    void setPrimaryClipInternal(PerUserClipboard clipboard, ClipData clip) {
+        clipboard.activePermissionOwners.clear();
+        if (clip == null && clipboard.primaryClip == null) {
+            return;
+        }
+        clipboard.primaryClip = clip;
+        final long ident = Binder.clearCallingIdentity();
+        final int n = clipboard.primaryClipListeners.beginBroadcast();
+        try {
+            for (int i = 0; i < n; i++) {
+                try {
+                    ListenerInfo li = (ListenerInfo)
+                            clipboard.primaryClipListeners.getBroadcastCookie(i);
+                    if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid,
+                            li.mPackageName) == AppOpsManager.MODE_ALLOWED) {
+                        clipboard.primaryClipListeners.getBroadcastItem(i)
+                                .dispatchPrimaryClipChanged();
+                    }
+                } catch (RemoteException e) {
+                    // The RemoteCallbackList will take care of removing
+                    // the dead object for us.
+                }
+            }
+        } finally {
+            clipboard.primaryClipListeners.finishBroadcast();
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
     
     public ClipData getPrimaryClip(String pkg) {
         synchronized (this) {
@@ -257,7 +313,8 @@
         try {
             // This will throw SecurityException for us.
             mAm.checkGrantUriPermission(uid, null, ContentProvider.getUriWithoutUserId(uri),
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION, resolveUserId(uri, uid));
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                    ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid)));
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -281,26 +338,26 @@
         }
     }
 
-    private final void grantUriLocked(Uri uri, String pkg) {
+    private final void grantUriLocked(Uri uri, String pkg, int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
+            int sourceUserId = ContentProvider.getUserIdFromUri(uri, userId);
+            uri = ContentProvider.getUriWithoutUserId(uri);
             mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg,
-                    ContentProvider.getUriWithoutUserId(uri),
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                    resolveUserId(uri, Process.myUid()));
+                    uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId);
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
-    private final void grantItemLocked(ClipData.Item item, String pkg) {
+    private final void grantItemLocked(ClipData.Item item, String pkg, int userId) {
         if (item.getUri() != null) {
-            grantUriLocked(item.getUri(), pkg);
+            grantUriLocked(item.getUri(), pkg, userId);
         }
         Intent intent = item.getIntent();
         if (intent != null && intent.getData() != null) {
-            grantUriLocked(intent.getData(), pkg);
+            grantUriLocked(intent.getData(), pkg, userId);
         }
     }
 
@@ -326,19 +383,21 @@
         if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
             final int N = clipboard.primaryClip.getItemCount();
             for (int i=0; i<N; i++) {
-                grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg);
+                grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg, UserHandle.getUserId(uid));
             }
             clipboard.activePermissionOwners.add(pkg);
         }
     }
 
     private final void revokeUriLocked(Uri uri) {
+        int userId = ContentProvider.getUserIdFromUri(uri,
+                UserHandle.getUserId(Binder.getCallingUid()));
         long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(mPermissionOwner,
-                    ContentProvider.getUriWithoutUserId(uri),
+            uri = ContentProvider.getUriWithoutUserId(uri);
+            mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    resolveUserId(uri, Process.myUid()));
+                    userId);
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -355,9 +414,7 @@
         }
     }
 
-    private final void clearActiveOwnersLocked() {
-        PerUserClipboard clipboard = getClipboard();
-        clipboard.activePermissionOwners.clear();
+    private final void revokeUris(PerUserClipboard clipboard) {
         if (clipboard.primaryClip == null) {
             return;
         }
@@ -366,8 +423,4 @@
             revokeItemLocked(clipboard.primaryClip.getItemAt(i));
         }
     }
-
-    private final int resolveUserId(Uri uri, int uid) {
-        return ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid));
-    }
 }