diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/content/ClipboardManager.java | 54 | ||||
| -rw-r--r-- | core/java/android/content/IClipboard.aidl | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 19 | ||||
| -rw-r--r-- | services/core/java/com/android/server/clipboard/ClipboardService.java | 152 |
5 files changed, 148 insertions, 79 deletions
diff --git a/api/current.txt b/api/current.txt index 1c97c664bce0..2a97e0083b30 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8972,6 +8972,7 @@ package android.content { public class ClipboardManager extends android.text.ClipboardManager { method public void addPrimaryClipChangedListener(android.content.ClipboardManager.OnPrimaryClipChangedListener); + method public void clearPrimaryClip(); method public android.content.ClipData getPrimaryClip(); method public android.content.ClipDescription getPrimaryClipDescription(); method public deprecated java.lang.CharSequence getText(); diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 718e465bf0de..73b6eb27bed3 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -16,13 +16,16 @@ package android.content; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemService; import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; /** @@ -45,6 +48,7 @@ import java.util.ArrayList; @SystemService(Context.CLIPBOARD_SERVICE) public class ClipboardManager extends android.text.ClipboardManager { private final Context mContext; + private final Handler mHandler; private final IClipboard mService; private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners @@ -52,20 +56,11 @@ public class ClipboardManager extends android.text.ClipboardManager { private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener = new IOnPrimaryClipChangedListener.Stub() { - public void dispatchPrimaryClipChanged() { - mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED); - } - }; - - static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1; - - private final Handler mHandler = new Handler() { @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_REPORT_PRIMARY_CLIP_CHANGED: - reportPrimaryClipChanged(); - } + public void dispatchPrimaryClipChanged() { + mHandler.post(() -> { + reportPrimaryClipChanged(); + }); } }; @@ -89,6 +84,7 @@ public class ClipboardManager extends android.text.ClipboardManager { /** {@hide} */ public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException { mContext = context; + mHandler = handler; mService = IClipboard.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE)); } @@ -98,12 +94,13 @@ public class ClipboardManager extends android.text.ClipboardManager { * is involved in normal cut and paste operations. * * @param clip The clipped data item to set. + * @see #getPrimaryClip() + * @see #clearPrimaryClip() */ - public void setPrimaryClip(ClipData clip) { + public void setPrimaryClip(@NonNull ClipData clip) { try { - if (clip != null) { - clip.prepareToLeaveProcess(true); - } + Preconditions.checkNotNull(clip); + clip.prepareToLeaveProcess(true); mService.setPrimaryClip(clip, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -111,9 +108,24 @@ public class ClipboardManager extends android.text.ClipboardManager { } /** + * Clears any current primary clip on the clipboard. + * + * @see #setPrimaryClip(ClipData) + */ + public void clearPrimaryClip() { + try { + mService.clearPrimaryClip(mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the current primary clip on the clipboard. + * + * @see #setPrimaryClip(ClipData) */ - public ClipData getPrimaryClip() { + public @Nullable ClipData getPrimaryClip() { try { return mService.getPrimaryClip(mContext.getOpPackageName()); } catch (RemoteException e) { @@ -124,8 +136,10 @@ public class ClipboardManager extends android.text.ClipboardManager { /** * Returns a description of the current primary clip on the clipboard * but not a copy of its data. + * + * @see #setPrimaryClip(ClipData) */ - public ClipDescription getPrimaryClipDescription() { + public @Nullable ClipDescription getPrimaryClipDescription() { try { return mService.getPrimaryClipDescription(mContext.getOpPackageName()); } catch (RemoteException e) { diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl index af0b8f0593a6..135a4363ef21 100644 --- a/core/java/android/content/IClipboard.aidl +++ b/core/java/android/content/IClipboard.aidl @@ -27,6 +27,7 @@ import android.content.IOnPrimaryClipChangedListener; */ interface IClipboard { void setPrimaryClip(in ClipData clip, String callingPackage); + void clearPrimaryClip(String callingPackage); ClipData getPrimaryClip(String pkg); ClipDescription getPrimaryClipDescription(String callingPackage); boolean hasPrimaryClip(String callingPackage); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d4307d72ac15..0f2a35e1732c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9409,6 +9409,25 @@ public class ActivityManagerService extends IActivityManager.Stub allowed = false; } } + if (pi.pathPermissions != null) { + final int N = pi.pathPermissions.length; + for (int i=0; i<N; i++) { + if (pi.pathPermissions[i] != null + && pi.pathPermissions[i].match(grantUri.uri.getPath())) { + if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { + if (pi.pathPermissions[i].getReadPermission() != null) { + allowed = false; + } + } + if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { + if (pi.pathPermissions[i].getWritePermission() != null) { + allowed = false; + } + } + break; + } + } + } if (allowed) { return -1; } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 0c9d70a95ab9..776e93dd053f 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -16,6 +16,7 @@ package com.android.server.clipboard; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -24,9 +25,9 @@ import android.app.KeyguardManager; import android.content.ClipData; import android.content.ClipDescription; import android.content.ContentProvider; +import android.content.Context; import android.content.IClipboard; import android.content.IOnPrimaryClipChangedListener; -import android.content.Context; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; @@ -37,7 +38,6 @@ 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; @@ -49,14 +49,10 @@ import android.util.SparseArray; import com.android.server.SystemService; -import java.util.HashSet; -import java.util.List; - -import java.lang.Thread; -import java.lang.Runnable; -import java.lang.InterruptedException; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.HashSet; +import java.util.List; // The following class is Android Emulator specific. It is used to read and // write contents of the host system's clipboard. @@ -182,7 +178,8 @@ public class ClipboardService extends SystemService { new String[]{"text/plain"}, new ClipData.Item(contents)); synchronized(mClipboards) { - setPrimaryClipInternal(getClipboard(0), clip); + setPrimaryClipInternal(getClipboard(0), clip, + android.os.Process.SYSTEM_UID); } } }); @@ -218,7 +215,10 @@ public class ClipboardService extends SystemService { final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners = new RemoteCallbackList<IOnPrimaryClipChangedListener>(); + /** Current primary clip. */ ClipData primaryClip; + /** UID that set {@link #primaryClip}. */ + int primaryClipUid = android.os.Process.NOBODY_UID; final HashSet<String> activePermissionOwners = new HashSet<String>(); @@ -246,58 +246,28 @@ public class ClipboardService extends SystemService { @Override public void setPrimaryClip(ClipData clip, String callingPackage) { synchronized (this) { - if (clip != null && clip.getItemCount() <= 0) { + if (clip == null || clip.getItemCount() <= 0) { throw new IllegalArgumentException("No items"); } - if (clip.getItemAt(0).getText() != null && - mHostClipboardMonitor != null) { - mHostClipboardMonitor.setHostClipboard( - clip.getItemAt(0).getText().toString()); - } final int callingUid = Binder.getCallingUid(); if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage, callingUid)) { return; } checkDataOwnerLocked(clip, callingUid); - 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 { - canCopy = !mUm.getUserRestrictions(userId).getBoolean( - UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE); - } catch (RemoteException e) { - 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 { - // We want to fix the uris of the related user's clip without changing the - // uris of the current user's clip. - // So, copy the ClipData, and then copy all the items, so that nothing - // is shared in memmory. - clip = new ClipData(clip); - for (int i = clip.getItemCount() - 1; i >= 0; i--) { - clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i))); - } - clip.fixUrisLight(userId); - } - for (int i = 0; i < size; i++) { - int id = related.get(i).id; - if (id != userId) { - setPrimaryClipInternal(getClipboard(id), clip); - } - } - } + setPrimaryClipInternal(clip, callingUid); + } + } + + @Override + public void clearPrimaryClip(String callingPackage) { + synchronized (this) { + final int callingUid = Binder.getCallingUid(); + if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage, + callingUid)) { + return; } + setPrimaryClipInternal(null, callingUid); } } @@ -398,13 +368,75 @@ public class ClipboardService extends SystemService { return related; } - void setPrimaryClipInternal(PerUserClipboard clipboard, ClipData clip) { + void setPrimaryClipInternal(@Nullable ClipData clip, int callingUid) { + // Push clipboard to host, if any + if (mHostClipboardMonitor != null) { + if (clip == null) { + // Someone really wants the clipboard cleared, so push empty + mHostClipboardMonitor.setHostClipboard(""); + } else if (clip.getItemCount() > 0) { + final CharSequence text = clip.getItemAt(0).getText(); + if (text != null) { + mHostClipboardMonitor.setHostClipboard(text.toString()); + } + } + } + + // Update this user + final int userId = UserHandle.getUserId(callingUid); + setPrimaryClipInternal(getClipboard(userId), clip, callingUid); + + // Update related users + 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 { + canCopy = !mUm.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE); + } catch (RemoteException e) { + 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 { + // We want to fix the uris of the related user's clip without changing the + // uris of the current user's clip. + // So, copy the ClipData, and then copy all the items, so that nothing + // is shared in memmory. + clip = new ClipData(clip); + for (int i = clip.getItemCount() - 1; i >= 0; i--) { + clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i))); + } + clip.fixUrisLight(userId); + } + for (int i = 0; i < size; i++) { + int id = related.get(i).id; + if (id != userId) { + setPrimaryClipInternal(getClipboard(id), clip, callingUid); + } + } + } + } + } + + void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, + int callingUid) { + revokeUris(clipboard); clipboard.activePermissionOwners.clear(); if (clip == null && clipboard.primaryClip == null) { return; } clipboard.primaryClip = clip; if (clip != null) { + clipboard.primaryClipUid = callingUid; + } else { + clipboard.primaryClipUid = android.os.Process.NOBODY_UID; + } + if (clip != null) { final ClipDescription description = clip.getDescription(); if (description != null) { description.setTimestamp(System.currentTimeMillis()); @@ -479,12 +511,12 @@ public class ClipboardService extends SystemService { } } - private final void grantUriLocked(Uri uri, String pkg, int userId) { + private final void grantUriLocked(Uri uri, int primaryClipUid, 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, + mAm.grantUriPermissionFromOwner(mPermissionOwner, primaryClipUid, pkg, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, userId); } catch (RemoteException e) { } finally { @@ -492,13 +524,14 @@ public class ClipboardService extends SystemService { } } - private final void grantItemLocked(ClipData.Item item, String pkg, int userId) { + private final void grantItemLocked(ClipData.Item item, int primaryClipUid, String pkg, + int userId) { if (item.getUri() != null) { - grantUriLocked(item.getUri(), pkg, userId); + grantUriLocked(item.getUri(), primaryClipUid, pkg, userId); } Intent intent = item.getIntent(); if (intent != null && intent.getData() != null) { - grantUriLocked(intent.getData(), pkg, userId); + grantUriLocked(intent.getData(), primaryClipUid, pkg, userId); } } @@ -524,7 +557,8 @@ public class ClipboardService extends SystemService { 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, UserHandle.getUserId(uid)); + grantItemLocked(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid, pkg, + UserHandle.getUserId(uid)); } clipboard.activePermissionOwners.add(pkg); } |