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

Commit a992999c authored by Oli Lan's avatar Oli Lan
Browse files

Simplify locking in ClipboardService.

Currently ClipboardService contains synchronization around two
objects: mLock, and the instance of ClipboardImpl. This
changes all the synchronization to use mLock, and applies
synchronization only where needed, i.e. when a guarded member
(such as the clipboard data) is being accessed.

Bug: 179667029
Test: atest ClipboardManagerTest; atest ClipDataTest; atest
ClipDescriptionTest

Change-Id: I7d55a2d5526275dc0e8ca1ce6fad9d4b14c6b3c8
parent 54c65485
Loading
Loading
Loading
Loading
+119 −117
Original line number Diff line number Diff line
@@ -179,8 +179,7 @@ public class ClipboardService extends SystemService {
    private final ContentCaptureManagerInternal mContentCaptureInternal;
    private final AutofillManagerInternal mAutofillInternal;
    private final IBinder mPermissionOwner;
    private HostClipboardMonitor mHostClipboardMonitor = null;
    private Thread mHostMonitorThread = null;
    private final HostClipboardMonitor mHostClipboardMonitor;

    @GuardedBy("mLock")
    private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -217,13 +216,15 @@ public class ClipboardService extends SystemService {
                                         new String[]{"text/plain"},
                                         new ClipData.Item(contents));
                        synchronized (mLock) {
                            setPrimaryClipInternal(getClipboard(0), clip,
                            setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
                                    android.os.Process.SYSTEM_UID, null);
                        }
                    }
                });
            mHostMonitorThread = new Thread(mHostClipboardMonitor);
            mHostMonitorThread.start();
            Thread hostMonitorThread = new Thread(mHostClipboardMonitor);
            hostMonitorThread.start();
        } else {
            mHostClipboardMonitor = null;
        }

        updateConfig();
@@ -378,9 +379,7 @@ public class ClipboardService extends SystemService {

        @Override
        public void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) {
            synchronized (this) {
                checkAndSetPrimaryClipLocked(clip, callingPackage, userId, callingPackage);
            }
            checkAndSetPrimaryClip(clip, callingPackage, userId, callingPackage);
        }

        @Override
@@ -388,14 +387,10 @@ public class ClipboardService extends SystemService {
                ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {
            getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
                    "Requires SET_CLIP_SOURCE permission");

            synchronized (this) {
                checkAndSetPrimaryClipLocked(clip, callingPackage, userId, sourcePackage);
            }
            checkAndSetPrimaryClip(clip, callingPackage, userId, sourcePackage);
        }

        @GuardedBy("this")
        private void checkAndSetPrimaryClipLocked(
        private void checkAndSetPrimaryClip(
                ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {
            if (clip == null || clip.getItemCount() <= 0) {
                throw new IllegalArgumentException("No items");
@@ -406,26 +401,27 @@ public class ClipboardService extends SystemService {
                    intendingUid, intendingUserId)) {
                return;
            }
            checkDataOwnerLocked(clip, intendingUid);
            setPrimaryClipInternal(clip, intendingUid, sourcePackage);
            checkDataOwner(clip, intendingUid);
            synchronized (mLock) {
                setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
            }
        }

        @Override
        public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
                    intendingUid, intendingUserId)) {
                return;
            }
                setPrimaryClipInternal(null, intendingUid, callingPackage);
            synchronized (mLock) {
                setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
            }
        }

        @Override
        public ClipData getPrimaryClip(String pkg, @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(pkg, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,
@@ -433,9 +429,10 @@ public class ClipboardService extends SystemService {
                    || isDeviceLocked(intendingUserId)) {
                return null;
            }
            synchronized (mLock) {
                addActiveOwnerLocked(intendingUid, pkg);
                PerUserClipboard clipboard = getClipboard(intendingUserId);
                maybeNotify(pkg, intendingUid, intendingUserId, clipboard);
                PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
                maybeNotifyLocked(pkg, intendingUid, intendingUserId, clipboard);
                return clipboard.primaryClip;
            }
        }
@@ -443,7 +440,6 @@ public class ClipboardService extends SystemService {
        @Override
        public ClipDescription getPrimaryClipDescription(String callingPackage,
                @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
@@ -451,7 +447,8 @@ public class ClipboardService extends SystemService {
                    || isDeviceLocked(intendingUserId)) {
                return null;
            }
                PerUserClipboard clipboard = getClipboard(intendingUserId);
            synchronized (mLock) {
                PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
                return clipboard.primaryClip != null
                        ? clipboard.primaryClip.getDescription() : null;
            }
@@ -459,7 +456,6 @@ public class ClipboardService extends SystemService {

        @Override
        public boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
@@ -467,17 +463,18 @@ public class ClipboardService extends SystemService {
                    || isDeviceLocked(intendingUserId)) {
                return false;
            }
                return getClipboard(intendingUserId).primaryClip != null;
            synchronized (mLock) {
                return getClipboardLocked(intendingUserId).primaryClip != null;
            }
        }

        @Override
        public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
                String callingPackage, @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
                getClipboard(intendingUserId).primaryClipListeners.register(listener,
            synchronized (mLock) {
                getClipboardLocked(intendingUserId).primaryClipListeners.register(listener,
                        new ListenerInfo(intendingUid, callingPackage));
            }
        }
@@ -485,15 +482,14 @@ public class ClipboardService extends SystemService {
        @Override
        public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
                String callingPackage, @UserIdInt int userId) {
            synchronized (this) {
            final int intendingUserId = getIntendingUserId(callingPackage, userId);
                getClipboard(intendingUserId).primaryClipListeners.unregister(listener);
            synchronized (mLock) {
                getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);
            }
        }

        @Override
        public boolean hasClipboardText(String callingPackage, int userId) {
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
@@ -501,7 +497,8 @@ public class ClipboardService extends SystemService {
                    || isDeviceLocked(intendingUserId)) {
                return false;
            }
                PerUserClipboard clipboard = getClipboard(intendingUserId);
            synchronized (mLock) {
                PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
                if (clipboard.primaryClip != null) {
                    CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
                    return text != null && text.length() > 0;
@@ -514,7 +511,6 @@ public class ClipboardService extends SystemService {
        public String getPrimaryClipSource(String callingPackage, int userId) {
            getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
                    "Requires SET_CLIP_SOURCE permission");
            synchronized (this) {
            final int intendingUid = getIntendingUid(callingPackage, userId);
            final int intendingUserId = UserHandle.getUserId(intendingUid);
            if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
@@ -522,7 +518,8 @@ public class ClipboardService extends SystemService {
                    || isDeviceLocked(intendingUserId)) {
                return null;
            }
                PerUserClipboard clipboard = getClipboard(intendingUserId);
            synchronized (mLock) {
                PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
                if (clipboard.primaryClip != null) {
                    return clipboard.mPrimaryClipPackage;
                }
@@ -531,8 +528,8 @@ public class ClipboardService extends SystemService {
        }
    };

    private PerUserClipboard getClipboard(@UserIdInt int userId) {
        synchronized (mLock) {
    @GuardedBy("mLock")
    private PerUserClipboard getClipboardLocked(@UserIdInt int userId) {
        PerUserClipboard puc = mClipboards.get(userId);
        if (puc == null) {
            puc = new PerUserClipboard(userId);
@@ -540,7 +537,6 @@ public class ClipboardService extends SystemService {
        }
        return puc;
    }
    }

    List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {
        final List<UserInfo> related;
@@ -570,10 +566,13 @@ public class ClipboardService extends SystemService {
    }

    void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
        setPrimaryClipInternal(clip, uid, null);
        synchronized (mLock) {
            setPrimaryClipInternalLocked(clip, uid, null);
        }
    }

    private void setPrimaryClipInternal(
    @GuardedBy("mLock")
    private void setPrimaryClipInternalLocked(
            @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
        // Push clipboard to host, if any
        if (mHostClipboardMonitor != null) {
@@ -590,7 +589,7 @@ public class ClipboardService extends SystemService {

        // Update this user
        final int userId = UserHandle.getUserId(uid);
        setPrimaryClipInternal(getClipboard(userId), clip, uid, sourcePackage);
        setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);

        // Update related users
        List<UserInfo> related = getRelatedProfiles(userId);
@@ -624,8 +623,8 @@ public class ClipboardService extends SystemService {
                        final boolean canCopyIntoProfile = !hasRestriction(
                                UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
                        if (canCopyIntoProfile) {
                            setPrimaryClipInternal(
                                    getClipboard(id), clip, uid, sourcePackage);
                            setPrimaryClipInternalLocked(
                                    getClipboardLocked(id), clip, uid, sourcePackage);
                        }
                    }
                }
@@ -635,10 +634,13 @@ public class ClipboardService extends SystemService {

    void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
            int uid) {
        setPrimaryClipInternal(clipboard, clip, uid, null);
        synchronized ("mLock") {
            setPrimaryClipInternalLocked(clipboard, clip, uid, null);
        }
    }

    private void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
    @GuardedBy("mLock")
    private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
            int uid, @Nullable String sourcePackage) {
        revokeUris(clipboard);
        clipboard.activePermissionOwners.clear();
@@ -694,7 +696,7 @@ public class ClipboardService extends SystemService {
        }
    }

    private final void checkUriOwnerLocked(Uri uri, int sourceUid) {
    private void checkUriOwner(Uri uri, int sourceUid) {
        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;

        final long ident = Binder.clearCallingIdentity();
@@ -709,24 +711,24 @@ public class ClipboardService extends SystemService {
        }
    }

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

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

    private final void grantUriLocked(Uri uri, int sourceUid, String targetPkg,
    private void grantUriPermission(Uri uri, int sourceUid, String targetPkg,
            int targetUserId) {
        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;

@@ -744,18 +746,19 @@ public class ClipboardService extends SystemService {
        }
    }

    private final void grantItemLocked(ClipData.Item item, int sourceUid, String targetPkg,
    private void grantItemPermission(ClipData.Item item, int sourceUid, String targetPkg,
            int targetUserId) {
        if (item.getUri() != null) {
            grantUriLocked(item.getUri(), sourceUid, targetPkg, targetUserId);
            grantUriPermission(item.getUri(), sourceUid, targetPkg, targetUserId);
        }
        Intent intent = item.getIntent();
        if (intent != null && intent.getData() != null) {
            grantUriLocked(intent.getData(), sourceUid, targetPkg, targetUserId);
            grantUriPermission(intent.getData(), sourceUid, targetPkg, targetUserId);
        }
    }

    private final void addActiveOwnerLocked(int uid, String pkg) {
    @GuardedBy("mLock")
    private void addActiveOwnerLocked(int uid, String pkg) {
        final IPackageManager pm = AppGlobals.getPackageManager();
        final int targetUserHandle = UserHandle.getCallingUserId();
        final long oldIdentity = Binder.clearCallingIdentity();
@@ -773,18 +776,18 @@ public class ClipboardService extends SystemService {
        } finally {
            Binder.restoreCallingIdentity(oldIdentity);
        }
        PerUserClipboard clipboard = getClipboard(UserHandle.getUserId(uid));
        PerUserClipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid));
        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), clipboard.primaryClipUid, pkg,
                        UserHandle.getUserId(uid));
                grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid,
                        pkg, UserHandle.getUserId(uid));
            }
            clipboard.activePermissionOwners.add(pkg);
        }
    }

    private final void revokeUriLocked(Uri uri, int sourceUid) {
    private void revokeUriPermission(Uri uri, int sourceUid) {
        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;

        final long ident = Binder.clearCallingIdentity();
@@ -798,23 +801,23 @@ public class ClipboardService extends SystemService {
        }
    }

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

    private final void revokeUris(PerUserClipboard clipboard) {
    private void revokeUris(PerUserClipboard clipboard) {
        if (clipboard.primaryClip == null) {
            return;
        }
        final int N = clipboard.primaryClip.getItemCount();
        for (int i=0; i<N; i++) {
            revokeItemLocked(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);
            revokeItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);
        }
    }

@@ -904,16 +907,15 @@ public class ClipboardService extends SystemService {
     * Potentially notifies the user (via a toast) about an app accessing the clipboard.
     * TODO(b/167676460): STOPSHIP as we don't want this code as-is to launch. Just an experiment.
     */
    private void maybeNotify(String callingPackage, int uid, @UserIdInt int userId,
    @GuardedBy("mLock")
    private void maybeNotifyLocked(String callingPackage, int uid, @UserIdInt int userId,
            PerUserClipboard clipboard) {
        if (clipboard.primaryClip == null) {
            return;
        }
        synchronized (mLock) {
        if (!mShowAccessNotifications) {
            return;
        }
        }
        // Don't notify if the app accessing the clipboard is the same as the current owner.
        if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
            return;