1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.clipboard;
18 
19 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
20 import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
21 import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
22 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
23 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
24 import static android.content.Context.DEVICE_ID_DEFAULT;
25 import static android.content.Context.DEVICE_ID_INVALID;
26 
27 import android.Manifest;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.UserIdInt;
31 import android.annotation.WorkerThread;
32 import android.app.ActivityManagerInternal;
33 import android.app.AppOpsManager;
34 import android.app.IUriGrantsManager;
35 import android.app.KeyguardManager;
36 import android.app.UriGrantsManager;
37 import android.companion.virtual.VirtualDeviceManager;
38 import android.content.BroadcastReceiver;
39 import android.content.ClipData;
40 import android.content.ClipDescription;
41 import android.content.ClipboardManager;
42 import android.content.ComponentName;
43 import android.content.ContentProvider;
44 import android.content.ContentResolver;
45 import android.content.Context;
46 import android.content.IClipboard;
47 import android.content.IOnPrimaryClipChangedListener;
48 import android.content.Intent;
49 import android.content.IntentFilter;
50 import android.content.pm.PackageManager;
51 import android.content.pm.PackageManagerInternal;
52 import android.content.pm.UserInfo;
53 import android.graphics.drawable.Drawable;
54 import android.hardware.display.DisplayManager;
55 import android.net.Uri;
56 import android.os.Binder;
57 import android.os.Build;
58 import android.os.Bundle;
59 import android.os.Handler;
60 import android.os.HandlerThread;
61 import android.os.IBinder;
62 import android.os.IUserManager;
63 import android.os.Looper;
64 import android.os.Message;
65 import android.os.Parcel;
66 import android.os.RemoteCallbackList;
67 import android.os.RemoteException;
68 import android.os.ServiceManager;
69 import android.os.UserHandle;
70 import android.os.UserManager;
71 import android.provider.DeviceConfig;
72 import android.provider.Settings;
73 import android.text.TextUtils;
74 import android.util.ArrayMap;
75 import android.util.ArraySet;
76 import android.util.Pair;
77 import android.util.SafetyProtectionUtils;
78 import android.util.Slog;
79 import android.util.SparseArrayMap;
80 import android.util.SparseBooleanArray;
81 import android.view.Display;
82 import android.view.autofill.AutofillManagerInternal;
83 import android.view.textclassifier.TextClassificationContext;
84 import android.view.textclassifier.TextClassificationManager;
85 import android.view.textclassifier.TextClassifier;
86 import android.view.textclassifier.TextClassifierEvent;
87 import android.view.textclassifier.TextLinks;
88 import android.widget.Toast;
89 
90 import com.android.internal.R;
91 import com.android.internal.annotations.GuardedBy;
92 import com.android.internal.annotations.VisibleForTesting;
93 import com.android.internal.util.FrameworkStatsLog;
94 import com.android.server.LocalServices;
95 import com.android.server.SystemService;
96 import com.android.server.UiThread;
97 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
98 import com.android.server.contentcapture.ContentCaptureManagerInternal;
99 import com.android.server.uri.UriGrantsManagerInternal;
100 import com.android.server.wm.WindowManagerInternal;
101 
102 import java.util.HashSet;
103 import java.util.List;
104 import java.util.function.Consumer;
105 
106 /**
107  * Implementation of the clipboard for copy and paste.
108  * <p>
109  * Caution: exception for clipboard data and isInternalSysWindowAppWithWindowFocus, any of data
110  * is accessed by userId or uid should be in * the try segment between
111  * Binder.clearCallingIdentity and Binder.restoreCallingIdentity.
112  * </p>
113  */
114 public class ClipboardService extends SystemService {
115 
116     private static final String TAG = "ClipboardService";
117 
118     @VisibleForTesting
119     public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000;
120 
121     /**
122      * Device config property for whether clipboard auto clear is enabled on the device
123      **/
124     public static final String PROPERTY_AUTO_CLEAR_ENABLED =
125             "auto_clear_enabled";
126 
127     /**
128      * Device config property for time period in milliseconds after which clipboard is auto
129      * cleared
130      **/
131     public static final String PROPERTY_AUTO_CLEAR_TIMEOUT =
132             "auto_clear_timeout";
133 
134     // DeviceConfig properties
135     private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
136     private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
137 
138     private final ActivityManagerInternal mAmInternal;
139     private final IUriGrantsManager mUgm;
140     private final UriGrantsManagerInternal mUgmInternal;
141     private final WindowManagerInternal mWm;
142     private final VirtualDeviceManagerInternal mVdmInternal;
143     private final VirtualDeviceManager mVdm;
144     private BroadcastReceiver mVirtualDeviceRemovedReceiver;
145     private VirtualDeviceManager.VirtualDeviceListener mVirtualDeviceListener;
146     private final IUserManager mUm;
147     private final PackageManager mPm;
148     private final AppOpsManager mAppOps;
149     private final ContentCaptureManagerInternal mContentCaptureInternal;
150     private final AutofillManagerInternal mAutofillInternal;
151     private final IBinder mPermissionOwner;
152     private final Consumer<ClipData> mClipboardMonitor;
153     private final Handler mWorkerHandler;
154 
155     @GuardedBy("mLock")
156     // Maps (userId, deviceId) to Clipboard.
157     private final SparseArrayMap<Integer, Clipboard> mClipboards = new SparseArrayMap<>();
158 
159     @GuardedBy("mLock")
160     private boolean mShowAccessNotifications =
161             ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
162     @GuardedBy("mLock")
163     private boolean mAllowVirtualDeviceSilos =
164             ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS;
165 
166     @GuardedBy("mLock")
167     private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
168 
169     private final Object mLock = new Object();
170 
171     /**
172      * Instantiates the clipboard.
173      */
ClipboardService(Context context)174     public ClipboardService(Context context) {
175         super(context);
176 
177         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
178         mUgm = UriGrantsManager.getService();
179         mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
180         mWm = LocalServices.getService(WindowManagerInternal.class);
181         // Can be null; not all products have CDM + VirtualDeviceManager
182         mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
183         mVdm = (mVdmInternal == null) ? null : getContext().getSystemService(
184                 VirtualDeviceManager.class);
185         mPm = getContext().getPackageManager();
186         mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
187         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
188         mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);
189         mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
190         final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
191         mPermissionOwner = permOwner;
192         if (Build.IS_EMULATOR) {
193             mClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
194                 synchronized (mLock) {
195                     Clipboard clipboard = getClipboardLocked(0, DEVICE_ID_DEFAULT);
196                     if (clipboard != null) {
197                         setPrimaryClipInternalLocked(clipboard, clip, android.os.Process.SYSTEM_UID,
198                                 null);
199                     }
200                 }
201             });
202         } else if (Build.IS_ARC) {
203             mClipboardMonitor = new ArcClipboardMonitor((clip, uid) -> {
204                 setPrimaryClipInternal(clip, uid);
205             });
206         } else {
207             mClipboardMonitor = (clip) -> {};
208         }
209 
210         updateConfig();
211         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,
212                 getContext().getMainExecutor(), properties -> updateConfig());
213 
214         HandlerThread workerThread = new HandlerThread(TAG);
215         workerThread.start();
216         mWorkerHandler = workerThread.getThreadHandler();
217     }
218 
219     @Override
onStart()220     public void onStart() {
221         publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());
222         if (!android.companion.virtual.flags.Flags.vdmPublicApis() && mVdmInternal != null) {
223             registerVirtualDeviceBroadcastReceiver();
224         } else if (android.companion.virtual.flags.Flags.vdmPublicApis() && mVdm != null) {
225             registerVirtualDeviceListener();
226         }
227     }
228 
registerVirtualDeviceBroadcastReceiver()229     private void registerVirtualDeviceBroadcastReceiver() {
230         if (mVirtualDeviceRemovedReceiver != null) {
231             return;
232         }
233         mVirtualDeviceRemovedReceiver = new BroadcastReceiver() {
234             @Override
235             public void onReceive(Context context, Intent intent) {
236                 if (!intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) {
237                     return;
238                 }
239                 final int removedDeviceId =
240                         intent.getIntExtra(EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_INVALID);
241                 synchronized (mLock) {
242                     for (int i = mClipboards.numMaps() - 1; i >= 0; i--) {
243                         mClipboards.delete(mClipboards.keyAt(i), removedDeviceId);
244                     }
245                 }
246             }
247         };
248         IntentFilter filter = new IntentFilter(ACTION_VIRTUAL_DEVICE_REMOVED);
249         getContext().registerReceiver(mVirtualDeviceRemovedReceiver, filter,
250                 Context.RECEIVER_NOT_EXPORTED);
251     }
252 
registerVirtualDeviceListener()253     private void registerVirtualDeviceListener() {
254         if (mVirtualDeviceListener != null) {
255             return;
256         }
257         mVirtualDeviceListener = new VirtualDeviceManager.VirtualDeviceListener() {
258             @Override
259             public void onVirtualDeviceClosed(int deviceId) {
260                 synchronized (mLock) {
261                     for (int i = mClipboards.numMaps() - 1; i >= 0; i--) {
262                         mClipboards.delete(mClipboards.keyAt(i), deviceId);
263                     }
264                 }
265             }
266         };
267         mVdm.registerVirtualDeviceListener(getContext().getMainExecutor(), mVirtualDeviceListener);
268     }
269 
270     @Override
onUserStopped(@onNull TargetUser user)271     public void onUserStopped(@NonNull TargetUser user) {
272         synchronized (mLock) {
273             mClipboards.delete(user.getUserIdentifier());
274         }
275     }
276 
updateConfig()277     private void updateConfig() {
278         synchronized (mLock) {
279             mShowAccessNotifications = DeviceConfig.getBoolean(
280                     DeviceConfig.NAMESPACE_CLIPBOARD,
281                     ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
282                     ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
283             mAllowVirtualDeviceSilos = DeviceConfig.getBoolean(
284                     DeviceConfig.NAMESPACE_CLIPBOARD,
285                     ClipboardManager.DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS,
286                     ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS);
287             mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
288                     PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
289         }
290     }
291 
292     private class ListenerInfo {
293         final int mUid;
294         final String mPackageName;
295         final String mAttributionTag;
296 
ListenerInfo(int uid, String packageName, String attributionTag)297         ListenerInfo(int uid, String packageName, String attributionTag) {
298             mUid = uid;
299             mPackageName = packageName;
300             mAttributionTag = attributionTag;
301         }
302     }
303 
304     private static class Clipboard {
305         public final int userId;
306         public final int deviceId;
307 
308         final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
309                 = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
310 
311         /** Current primary clip. */
312         ClipData primaryClip;
313         /** UID that set {@link #primaryClip}. */
314         int primaryClipUid = android.os.Process.NOBODY_UID;
315         /** Package of the app that set {@link #primaryClip}. */
316         String mPrimaryClipPackage;
317 
318         /** Uids that have already triggered a toast notification for {@link #primaryClip} */
319         final SparseBooleanArray mNotifiedUids = new SparseBooleanArray();
320 
321         /**
322          * Uids that have already triggered a notification to text classifier for
323          * {@link #primaryClip}.
324          */
325         final SparseBooleanArray mNotifiedTextClassifierUids = new SparseBooleanArray();
326 
327         final HashSet<String> activePermissionOwners
328                 = new HashSet<String>();
329 
330         /** The text classifier session that is used to annotate the text in the primary clip. */
331         TextClassifier mTextClassifier;
332 
Clipboard(int userId, int deviceId)333         Clipboard(int userId, int deviceId) {
334             this.userId = userId;
335             this.deviceId = deviceId;
336         }
337     }
338 
339     /**
340      * To check if the application has granted the INTERNAL_SYSTEM_WINDOW permission and window
341      * focus.
342      * <p>
343      * All of applications granted INTERNAL_SYSTEM_WINDOW has the risk to leak clip information to
344      * the other user because INTERNAL_SYSTEM_WINDOW is signature level. i.e. platform key. Because
345      * some of applications have both of INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL at
346      * the same time, that means they show the same window to all of users.
347      * </p><p>
348      * Unfortunately, all of applications with INTERNAL_SYSTEM_WINDOW starts very early and then
349      * the real window show is belong to user 0 rather user X. The result of
350      * WindowManager.isUidFocused checking user X window is false.
351      * </p>
352      * @return true if the app granted INTERNAL_SYSTEM_WINDOW permission.
353      */
isInternalSysWindowAppWithWindowFocus(String callingPackage)354     private boolean isInternalSysWindowAppWithWindowFocus(String callingPackage) {
355         // Shell can access the clipboard for testing purposes.
356         if (mPm.checkPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,
357                     callingPackage) == PackageManager.PERMISSION_GRANTED) {
358             if (mWm.isUidFocused(Binder.getCallingUid())) {
359                 return true;
360             }
361         }
362 
363         return false;
364     }
365 
366     /**
367      * To get the validate current userId.
368      * <p>
369      * The intending userId needs to be validated by ActivityManagerInternal.handleIncomingUser.
370      * To check if the uid of the process have the permission to run as the userId.
371      * e.x. INTERACT_ACROSS_USERS_FULL or INTERACT_ACROSS_USERS permission granted.
372      * </p>
373      * <p>
374      * The application with the granted INTERNAL_SYSTEM_WINDOW permission should run as the output
375      * of ActivityManagerInternal.handleIncomingUser rather the userId of Binder.getCAllingUid().
376      * To use the userId of Binder.getCallingUid() is the root cause that leaks the information
377      * comes from user 0 to user X.
378      * </p>
379      *
380      * @param packageName the package name of the calling side
381      * @param userId the userId passed by the calling side
382      * @return return the intending userId that has been validated by ActivityManagerInternal.
383      */
384     @UserIdInt
getIntendingUserId(String packageName, @UserIdInt int userId)385     private int getIntendingUserId(String packageName, @UserIdInt int userId) {
386         final int callingUid = Binder.getCallingUid();
387         final int callingUserId = UserHandle.getUserId(callingUid);
388         if (!UserManager.supportsMultipleUsers() || callingUserId == userId) {
389             return callingUserId;
390         }
391 
392         int intendingUserId = callingUserId;
393         intendingUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
394                 Binder.getCallingUid(), userId, false /* allow all */, ALLOW_FULL_ONLY,
395                 "checkClipboardServiceCallingUser", packageName);
396 
397         return intendingUserId;
398     }
399 
400     /**
401      * To get the current running uid who is intend to run as.
402      * In ording to distinguish the nameing and reducing the confusing names, the client client
403      * side pass userId that is intend to run as,
404      * @return return IntentingUid = validated intenting userId +
405      *         UserHandle.getAppId(Binder.getCallingUid())
406      */
getIntendingUid(String packageName, @UserIdInt int userId)407     private int getIntendingUid(String packageName, @UserIdInt int userId) {
408         return UserHandle.getUid(getIntendingUserId(packageName, userId),
409                 UserHandle.getAppId(Binder.getCallingUid()));
410     }
411 
412     /**
413      * Determines which deviceId to use for selecting a Clipboard, depending on where a given app
414      * is running and the device's clipboard policy.
415      *
416      * @param requestedDeviceId the requested deviceId passed in from the client side
417      * @param uid the intended app uid
418      * @return a deviceId to use in selecting the appropriate clipboard, or
419      * DEVICE_ID_INVALID if this uid should not be allowed access. A value of DEVICE_ID_DEFAULT
420      * means just use the "regular" clipboard.
421      */
getIntendingDeviceId(int requestedDeviceId, int uid)422     private int getIntendingDeviceId(int requestedDeviceId, int uid) {
423         if (mVdmInternal == null) {
424             return DEVICE_ID_DEFAULT;
425         }
426 
427         ArraySet<Integer> virtualDeviceIds = mVdmInternal.getDeviceIdsForUid(uid);
428 
429         synchronized (mLock) {
430             if (!mAllowVirtualDeviceSilos
431                     && (!virtualDeviceIds.isEmpty() || requestedDeviceId != DEVICE_ID_DEFAULT)) {
432                 return DEVICE_ID_INVALID;
433             }
434         }
435 
436         // If an app is running on any VirtualDevice, it isn't clear which clipboard they
437         // should use, unless all of the devices share the default device's clipboard.
438         boolean allDevicesHaveDefaultClipboard = true;
439         for (int deviceId : virtualDeviceIds) {
440             if (!deviceUsesDefaultClipboard(deviceId)) {
441                 allDevicesHaveDefaultClipboard = false;
442                 break;
443             }
444         }
445 
446         // Apps running on a virtual device may get the default clipboard if all the devices the app
447         // runs on share that clipboard. Otherwise it's not clear which clipboard to use.
448         if (requestedDeviceId == DEVICE_ID_DEFAULT) {
449             return allDevicesHaveDefaultClipboard ? DEVICE_ID_DEFAULT : DEVICE_ID_INVALID;
450         }
451 
452         // At this point the app wants to access a virtual device clipboard. It may do so if:
453         //  1. The app owns the VirtualDevice
454         //  2. The app is present on the VirtualDevice
455         //  3. The VirtualDevice shares the default device clipboard and all virtual devices that
456         //     the app is running on do the same.
457         int clipboardDeviceId = deviceUsesDefaultClipboard(requestedDeviceId)
458                 ? DEVICE_ID_DEFAULT
459                 : requestedDeviceId;
460 
461         if (mVdmInternal.getDeviceOwnerUid(requestedDeviceId) == uid
462                 || virtualDeviceIds.contains(requestedDeviceId)
463                 || (clipboardDeviceId == DEVICE_ID_DEFAULT && allDevicesHaveDefaultClipboard)) {
464             return clipboardDeviceId;
465         }
466 
467         // Fallback to the device where the app is running, unless it uses the default clipboard.
468         int fallbackDeviceId = virtualDeviceIds.valueAt(0);
469         return deviceUsesDefaultClipboard(fallbackDeviceId) ? DEVICE_ID_DEFAULT : fallbackDeviceId;
470     }
471 
deviceUsesDefaultClipboard(int deviceId)472     private boolean deviceUsesDefaultClipboard(int deviceId) {
473         if (deviceId == DEVICE_ID_DEFAULT || mVdm == null) {
474             return true;
475         }
476         return mVdm.getDevicePolicy(deviceId, POLICY_TYPE_CLIPBOARD) == DEVICE_POLICY_CUSTOM;
477     }
478 
479     /**
480      * To handle the difference between userId and intendingUserId, uid and intendingUid.
481      *
482      * userId means that comes from the calling side and should be validated by
483      * ActivityManagerInternal.handleIncomingUser.
484      * After validation of ActivityManagerInternal.handleIncomingUser, the userId is called
485      * 'intendingUserId' and the uid is called 'intendingUid'.
486      */
487     private class ClipboardImpl extends IClipboard.Stub {
488 
489         private final Handler mClipboardClearHandler = new ClipboardClearHandler(
490                 mWorkerHandler.getLooper());
491 
492         @Override
onTransact(int code, Parcel data, Parcel reply, int flags)493         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
494                 throws RemoteException {
495             try {
496                 return super.onTransact(code, data, reply, flags);
497             } catch (RuntimeException e) {
498                 if (!(e instanceof SecurityException)) {
499                     Slog.wtf("clipboard", "Exception: ", e);
500                 }
501                 throw e;
502             }
503 
504         }
505 
506         @Override
setPrimaryClip( ClipData clip, String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)507         public void setPrimaryClip(
508                 ClipData clip,
509                 String callingPackage,
510                 String attributionTag,
511                 @UserIdInt int userId,
512                 int deviceId) {
513             checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
514                     callingPackage);
515         }
516 
517         @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE)
518         @Override
setPrimaryClipAsPackage( ClipData clip, String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId, String sourcePackage)519         public void setPrimaryClipAsPackage(
520                 ClipData clip,
521                 String callingPackage,
522                 String attributionTag,
523                 @UserIdInt int userId,
524                 int deviceId,
525                 String sourcePackage) {
526             setPrimaryClipAsPackage_enforcePermission();
527             checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
528                     sourcePackage);
529         }
530 
531         @Override
areClipboardAccessNotificationsEnabledForUser(int userId)532         public boolean areClipboardAccessNotificationsEnabledForUser(int userId) {
533             int result = getContext().checkCallingOrSelfPermission(
534                     Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION);
535             if (result != PackageManager.PERMISSION_GRANTED) {
536                 throw new SecurityException("areClipboardAccessNotificationsEnable requires "
537                         + "permission MANAGE_CLIPBOARD_ACCESS_NOTIFICATION");
538             }
539 
540             long callingId = Binder.clearCallingIdentity();
541             try {
542                 return Settings.Secure.getIntForUser(getContext().getContentResolver(),
543                         Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
544                         getDefaultClipboardAccessNotificationsSetting(), userId) != 0;
545             } finally {
546                 Binder.restoreCallingIdentity(callingId);
547             }
548         }
549 
550         @Override
setClipboardAccessNotificationsEnabledForUser(boolean enable, int userId)551         public void setClipboardAccessNotificationsEnabledForUser(boolean enable, int userId) {
552             int result = getContext().checkCallingOrSelfPermission(
553                     Manifest.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION);
554             if (result != PackageManager.PERMISSION_GRANTED) {
555                 throw new SecurityException("areClipboardAccessNotificationsEnable requires "
556                         + "permission MANAGE_CLIPBOARD_ACCESS_NOTIFICATION");
557             }
558 
559             long callingId = Binder.clearCallingIdentity();
560             try {
561                 ContentResolver resolver = getContext()
562                         .createContextAsUser(UserHandle.of(userId), 0).getContentResolver();
563                 Settings.Secure.putInt(resolver,
564                         Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, (enable ? 1 : 0));
565             } finally {
566                 Binder.restoreCallingIdentity(callingId);
567             }
568         }
569 
getDefaultClipboardAccessNotificationsSetting()570         private int getDefaultClipboardAccessNotificationsSetting() {
571             return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
572                     ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
573                     ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS) ? 1 : 0;
574         }
575 
checkAndSetPrimaryClip( ClipData clip, String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId, String sourcePackage)576         private void checkAndSetPrimaryClip(
577                 ClipData clip,
578                 String callingPackage,
579                 String attributionTag,
580                 @UserIdInt int userId,
581                 int deviceId,
582                 String sourcePackage) {
583             if (clip == null || clip.getItemCount() <= 0) {
584                 throw new IllegalArgumentException("No items");
585             }
586             final int intendingUid = getIntendingUid(callingPackage, userId);
587             final int intendingUserId = UserHandle.getUserId(intendingUid);
588             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
589             if (!clipboardAccessAllowed(
590                     AppOpsManager.OP_WRITE_CLIPBOARD,
591                     callingPackage,
592                     attributionTag,
593                     intendingUid,
594                     intendingUserId,
595                     intendingDeviceId)) {
596                 return;
597             }
598             checkDataOwner(clip, intendingUid);
599             synchronized (mLock) {
600                 scheduleAutoClear(userId, intendingUid, intendingDeviceId);
601                 setPrimaryClipInternalLocked(clip, intendingUid, intendingDeviceId, sourcePackage);
602             }
603         }
604 
scheduleAutoClear( @serIdInt int userId, int intendingUid, int intendingDeviceId)605         private void scheduleAutoClear(
606                 @UserIdInt int userId, int intendingUid, int intendingDeviceId) {
607             final long oldIdentity = Binder.clearCallingIdentity();
608             try {
609                 if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
610                         PROPERTY_AUTO_CLEAR_ENABLED, true)) {
611                     Pair<Integer, Integer> userIdDeviceId = new Pair<>(userId, intendingDeviceId);
612                     mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
613                             userIdDeviceId);
614                     Message clearMessage =
615                             Message.obtain(
616                                     mClipboardClearHandler,
617                                     ClipboardClearHandler.MSG_CLEAR,
618                                     userId,
619                                     intendingUid,
620                                     userIdDeviceId);
621                     mClipboardClearHandler.sendMessageDelayed(clearMessage,
622                             getTimeoutForAutoClear());
623                 }
624             } finally {
625                 Binder.restoreCallingIdentity(oldIdentity);
626             }
627         }
628 
getTimeoutForAutoClear()629         private long getTimeoutForAutoClear() {
630             return DeviceConfig.getLong(DeviceConfig.NAMESPACE_CLIPBOARD,
631                     PROPERTY_AUTO_CLEAR_TIMEOUT,
632                     DEFAULT_CLIPBOARD_TIMEOUT_MILLIS);
633         }
634 
635         @Override
clearPrimaryClip( String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)636         public void clearPrimaryClip(
637                 String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
638             final int intendingUid = getIntendingUid(callingPackage, userId);
639             final int intendingUserId = UserHandle.getUserId(intendingUid);
640             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
641             if (!clipboardAccessAllowed(
642                     AppOpsManager.OP_WRITE_CLIPBOARD,
643                     callingPackage,
644                     attributionTag,
645                     intendingUid,
646                     intendingUserId,
647                     intendingDeviceId)) {
648                 return;
649             }
650             synchronized (mLock) {
651                 mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
652                         new Pair<>(userId, deviceId));
653                 setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, callingPackage);
654             }
655         }
656 
657         @Override
getPrimaryClip( String pkg, String attributionTag, @UserIdInt int userId, int deviceId)658         public ClipData getPrimaryClip(
659                 String pkg, String attributionTag, @UserIdInt int userId, int deviceId) {
660             final int intendingUid = getIntendingUid(pkg, userId);
661             final int intendingUserId = UserHandle.getUserId(intendingUid);
662             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
663             if (!clipboardAccessAllowed(
664                             AppOpsManager.OP_READ_CLIPBOARD,
665                             pkg,
666                             attributionTag,
667                             intendingUid,
668                             intendingUserId,
669                             intendingDeviceId)
670                     || isDeviceLocked(intendingUserId, deviceId)) {
671                 return null;
672             }
673             synchronized (mLock) {
674                 try {
675                     addActiveOwnerLocked(intendingUid, intendingDeviceId, pkg);
676                 } catch (SecurityException e) {
677                     // Permission could not be granted - URI may be invalid
678                     Slog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");
679                     setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, pkg);
680                     return null;
681                 }
682 
683                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
684                 if (clipboard == null) {
685                     return null;
686                 }
687                 showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
688                 notifyTextClassifierLocked(clipboard, pkg, intendingUid);
689                 if (clipboard.primaryClip != null) {
690                     scheduleAutoClear(userId, intendingUid, intendingDeviceId);
691                 }
692                 return clipboard.primaryClip;
693             }
694         }
695 
696         @Override
getPrimaryClipDescription( String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)697         public ClipDescription getPrimaryClipDescription(
698                 String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
699             final int intendingUid = getIntendingUid(callingPackage, userId);
700             final int intendingUserId = UserHandle.getUserId(intendingUid);
701             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
702             if (!clipboardAccessAllowed(
703                             AppOpsManager.OP_READ_CLIPBOARD,
704                             callingPackage,
705                             attributionTag,
706                             intendingUid,
707                             intendingUserId,
708                             intendingDeviceId,
709                             false)
710                     || isDeviceLocked(intendingUserId, deviceId)) {
711                 return null;
712             }
713             synchronized (mLock) {
714                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
715                 return (clipboard != null && clipboard.primaryClip != null)
716                         ? clipboard.primaryClip.getDescription() : null;
717             }
718         }
719 
720         @Override
hasPrimaryClip( String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)721         public boolean hasPrimaryClip(
722                 String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
723             final int intendingUid = getIntendingUid(callingPackage, userId);
724             final int intendingUserId = UserHandle.getUserId(intendingUid);
725             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
726             if (!clipboardAccessAllowed(
727                             AppOpsManager.OP_READ_CLIPBOARD,
728                             callingPackage,
729                             attributionTag,
730                             intendingUid,
731                             intendingUserId,
732                             intendingDeviceId,
733                             false)
734                     || isDeviceLocked(intendingUserId, deviceId)) {
735                 return false;
736             }
737             synchronized (mLock) {
738                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
739                 return clipboard != null && clipboard.primaryClip != null;
740             }
741         }
742 
743         @Override
addPrimaryClipChangedListener( IOnPrimaryClipChangedListener listener, String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)744         public void addPrimaryClipChangedListener(
745                 IOnPrimaryClipChangedListener listener,
746                 String callingPackage,
747                 String attributionTag,
748                 @UserIdInt int userId,
749                 int deviceId) {
750             final int intendingUid = getIntendingUid(callingPackage, userId);
751             final int intendingUserId = UserHandle.getUserId(intendingUid);
752             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
753             if (intendingDeviceId == DEVICE_ID_INVALID) {
754                 Slog.i(TAG, "addPrimaryClipChangedListener invalid deviceId for userId:"
755                         + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage
756                         + " requestedDeviceId:" + deviceId);
757                 return;
758             }
759             synchronized (mLock) {
760                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
761                 if (clipboard == null) {
762                     return;
763                 }
764                 clipboard.primaryClipListeners
765                         .register(
766                                 listener,
767                                 new ListenerInfo(intendingUid, callingPackage, attributionTag));
768             }
769         }
770 
771         @Override
removePrimaryClipChangedListener( IOnPrimaryClipChangedListener listener, String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId)772         public void removePrimaryClipChangedListener(
773                 IOnPrimaryClipChangedListener listener,
774                 String callingPackage,
775                 String attributionTag,
776                 @UserIdInt int userId,
777                 int deviceId) {
778             final int intendingUid = getIntendingUid(callingPackage, userId);
779             final int intendingUserId = getIntendingUserId(callingPackage, userId);
780             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
781             if (intendingDeviceId == DEVICE_ID_INVALID) {
782                 Slog.i(TAG, "removePrimaryClipChangedListener invalid deviceId for userId:"
783                         + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage);
784                 return;
785             }
786             synchronized (mLock) {
787                 Clipboard clipboard = getClipboardLocked(intendingUserId,
788                                 intendingDeviceId);
789                 if (clipboard != null) {
790                     clipboard.primaryClipListeners.unregister(listener);
791                 }
792             }
793         }
794 
795         @Override
hasClipboardText( String callingPackage, String attributionTag, int userId, int deviceId)796         public boolean hasClipboardText(
797                 String callingPackage, String attributionTag, int userId, int deviceId) {
798             final int intendingUid = getIntendingUid(callingPackage, userId);
799             final int intendingUserId = UserHandle.getUserId(intendingUid);
800             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
801             if (!clipboardAccessAllowed(
802                             AppOpsManager.OP_READ_CLIPBOARD,
803                             callingPackage,
804                             attributionTag,
805                             intendingUid,
806                             intendingUserId,
807                             intendingDeviceId,
808                             false)
809                     || isDeviceLocked(intendingUserId, deviceId)) {
810                 return false;
811             }
812             synchronized (mLock) {
813                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
814                 if (clipboard != null && clipboard.primaryClip != null) {
815                     CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
816                     return text != null && text.length() > 0;
817                 }
818                 return false;
819             }
820         }
821 
822         @android.annotation.EnforcePermission(android.Manifest.permission.SET_CLIP_SOURCE)
823         @Override
getPrimaryClipSource( String callingPackage, String attributionTag, int userId, int deviceId)824         public String getPrimaryClipSource(
825                 String callingPackage, String attributionTag, int userId, int deviceId) {
826             getPrimaryClipSource_enforcePermission();
827             final int intendingUid = getIntendingUid(callingPackage, userId);
828             final int intendingUserId = UserHandle.getUserId(intendingUid);
829             final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
830             if (!clipboardAccessAllowed(
831                             AppOpsManager.OP_READ_CLIPBOARD,
832                             callingPackage,
833                             attributionTag,
834                             intendingUid,
835                             intendingUserId,
836                             intendingDeviceId,
837                             false)
838                     || isDeviceLocked(intendingUserId, deviceId)) {
839                 return null;
840             }
841             synchronized (mLock) {
842                 Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
843                 if (clipboard != null && clipboard.primaryClip != null) {
844                     return clipboard.mPrimaryClipPackage;
845                 }
846                 return null;
847             }
848         }
849 
850         private class ClipboardClearHandler extends Handler {
851 
852             public static final int MSG_CLEAR = 101;
853 
ClipboardClearHandler(Looper looper)854             ClipboardClearHandler(Looper looper) {
855                 super(looper);
856             }
857 
handleMessage(@onNull Message msg)858             public void handleMessage(@NonNull Message msg) {
859                 switch (msg.what) {
860                     case MSG_CLEAR:
861                         final int userId = msg.arg1;
862                         final int intendingUid = msg.arg2;
863                         final int intendingDeviceId = ((Pair<Integer, Integer>) msg.obj).second;
864                         synchronized (mLock) {
865                             Clipboard clipboard = getClipboardLocked(userId, intendingDeviceId);
866                             if (clipboard != null && clipboard.primaryClip != null) {
867                                 FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
868                                         FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
869                                 setPrimaryClipInternalLocked(
870                                         null, intendingUid, intendingDeviceId, null);
871                             }
872                         }
873                         break;
874                     default:
875                         Slog.wtf(TAG, "ClipboardClearHandler received unknown message " + msg.what);
876                 }
877             }
878         }
879     };
880 
881     @GuardedBy("mLock")
getClipboardLocked(@serIdInt int userId, int deviceId)882     private @Nullable Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) {
883         Clipboard clipboard = mClipboards.get(userId, deviceId);
884         if (clipboard == null) {
885             try {
886                 if (!mUm.isUserRunning(userId)) {
887                     Slog.w(TAG, "getClipboardLocked called with not running userId " + userId);
888                     return null;
889                 }
890             } catch (RemoteException e) {
891                 Slog.e(TAG, "RemoteException calling UserManager: " + e);
892                 return null;
893             }
894             if (deviceId != DEVICE_ID_DEFAULT && !mVdm.isValidVirtualDeviceId(deviceId)) {
895                 Slog.w(TAG, "getClipboardLocked called with invalid (possibly released) deviceId "
896                         + deviceId);
897                 return null;
898             }
899             clipboard = new Clipboard(userId, deviceId);
900             mClipboards.add(userId, deviceId, clipboard);
901         }
902         return clipboard;
903     }
904 
getRelatedProfiles(@serIdInt int userId)905     List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {
906         final List<UserInfo> related;
907         final long origId = Binder.clearCallingIdentity();
908         try {
909             related = mUm.getProfiles(userId, true);
910         } catch (RemoteException e) {
911             Slog.e(TAG, "Remote Exception calling UserManager: " + e);
912             return null;
913         } finally{
914             Binder.restoreCallingIdentity(origId);
915         }
916         return related;
917     }
918 
919     /** Check if the user has the given restriction set. Default to true if error occured during
920      * calling UserManager, so it fails safe.
921      */
hasRestriction(String restriction, int userId)922     private boolean hasRestriction(String restriction, int userId) {
923         try {
924             return mUm.hasUserRestriction(restriction, userId);
925         } catch (RemoteException e) {
926             Slog.e(TAG, "Remote Exception calling UserManager.getUserRestrictions: ", e);
927             // Fails safe
928             return true;
929         }
930     }
931 
setPrimaryClipInternal(@ullable ClipData clip, int uid)932     void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
933         synchronized (mLock) {
934             setPrimaryClipInternalLocked(clip, uid, DEVICE_ID_DEFAULT, null);
935         }
936     }
937 
938     @GuardedBy("mLock")
setPrimaryClipInternalLocked( @ullable ClipData clip, int uid, int deviceId, @Nullable String sourcePackage)939     private void setPrimaryClipInternalLocked(
940             @Nullable ClipData clip, int uid, int deviceId, @Nullable String sourcePackage) {
941         if (deviceId == DEVICE_ID_DEFAULT) {
942             mClipboardMonitor.accept(clip);
943         }
944 
945         final int userId = UserHandle.getUserId(uid);
946 
947         // Update this user
948         Clipboard clipboard = getClipboardLocked(userId, deviceId);
949         if (clipboard == null) {
950             return;
951         }
952         setPrimaryClipInternalLocked(clipboard, clip, uid, sourcePackage);
953 
954         // Update related users
955         List<UserInfo> related = getRelatedProfiles(userId);
956         if (related != null) {
957             int size = related.size();
958             if (size > 1) { // Related profiles list include the current profile.
959                 final boolean canCopy = !hasRestriction(
960                         UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, userId);
961                 // Copy clip data to related users if allowed. If disallowed, then remove
962                 // primary clip in related users to prevent pasting stale content.
963                 if (!canCopy) {
964                     clip = null;
965                 } else if (clip == null) {
966                     // do nothing for canCopy == true and clip == null case
967                     // To prevent from NPE happen in 'new ClipData(clip)' when run
968                     // android.content.cts.ClipboardManagerTest#testClearPrimaryClip
969                 } else {
970                     // We want to fix the uris of the related user's clip without changing the
971                     // uris of the current user's clip.
972                     // So, copy the ClipData, and then copy all the items, so that nothing
973                     // is shared in memory.
974                     clip = new ClipData(clip);
975                     for (int i = clip.getItemCount() - 1; i >= 0; i--) {
976                         clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
977                     }
978                     clip.fixUrisLight(userId);
979                 }
980                 for (int i = 0; i < size; i++) {
981                     int id = related.get(i).id;
982                     if (id != userId) {
983                         final boolean canCopyIntoProfile = !hasRestriction(
984                                 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
985                         if (canCopyIntoProfile) {
986                             Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
987                             if (relatedClipboard != null) {
988                                 setPrimaryClipInternalNoClassifyLocked(relatedClipboard, clip, uid,
989                                         sourcePackage);
990                             }
991                         }
992                     }
993                 }
994             }
995         }
996     }
997 
setPrimaryClipInternal(Clipboard clipboard, @Nullable ClipData clip, int uid)998     void setPrimaryClipInternal(Clipboard clipboard, @Nullable ClipData clip,
999             int uid) {
1000         synchronized (mLock) {
1001             setPrimaryClipInternalLocked(clipboard, clip, uid, null);
1002         }
1003     }
1004 
1005     @GuardedBy("mLock")
setPrimaryClipInternalLocked(Clipboard clipboard, @Nullable ClipData clip, int uid, @Nullable String sourcePackage)1006     private void setPrimaryClipInternalLocked(Clipboard clipboard, @Nullable ClipData clip,
1007             int uid, @Nullable String sourcePackage) {
1008         final int userId = UserHandle.getUserId(uid);
1009         if (clip != null) {
1010             startClassificationLocked(clip, userId, clipboard.deviceId);
1011         }
1012 
1013         setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
1014     }
1015 
1016     @GuardedBy("mLock")
setPrimaryClipInternalNoClassifyLocked(Clipboard clipboard, @Nullable ClipData clip, int uid, @Nullable String sourcePackage)1017     private void setPrimaryClipInternalNoClassifyLocked(Clipboard clipboard,
1018             @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
1019         revokeUris(clipboard);
1020         clipboard.activePermissionOwners.clear();
1021         if (clip == null && clipboard.primaryClip == null) {
1022             return;
1023         }
1024         clipboard.primaryClip = clip;
1025         clipboard.mNotifiedUids.clear();
1026         clipboard.mNotifiedTextClassifierUids.clear();
1027         if (clip != null) {
1028             clipboard.primaryClipUid = uid;
1029             clipboard.mPrimaryClipPackage = sourcePackage;
1030         } else {
1031             clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
1032             clipboard.mPrimaryClipPackage = null;
1033         }
1034         if (clip != null) {
1035             final ClipDescription description = clip.getDescription();
1036             if (description != null) {
1037                 description.setTimestamp(System.currentTimeMillis());
1038             }
1039         }
1040         sendClipChangedBroadcast(clipboard);
1041     }
1042 
sendClipChangedBroadcast(Clipboard clipboard)1043     private void sendClipChangedBroadcast(Clipboard clipboard) {
1044         final long ident = Binder.clearCallingIdentity();
1045         final int n = clipboard.primaryClipListeners.beginBroadcast();
1046         try {
1047             for (int i = 0; i < n; i++) {
1048                 try {
1049                     ListenerInfo li = (ListenerInfo)
1050                             clipboard.primaryClipListeners.getBroadcastCookie(i);
1051 
1052                     if (clipboardAccessAllowed(
1053                             AppOpsManager.OP_READ_CLIPBOARD,
1054                             li.mPackageName,
1055                             li.mAttributionTag,
1056                             li.mUid,
1057                             UserHandle.getUserId(li.mUid),
1058                             clipboard.deviceId)) {
1059                         clipboard.primaryClipListeners.getBroadcastItem(i)
1060                                 .dispatchPrimaryClipChanged();
1061                     }
1062                 } catch (RemoteException | SecurityException e) {
1063                     // The RemoteCallbackList will take care of removing
1064                     // the dead object for us.
1065                 }
1066             }
1067         } finally {
1068             clipboard.primaryClipListeners.finishBroadcast();
1069             Binder.restoreCallingIdentity(ident);
1070         }
1071     }
1072 
1073     @GuardedBy("mLock")
startClassificationLocked(@onNull ClipData clip, @UserIdInt int userId, int deviceId)1074     private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId,
1075             int deviceId) {
1076         CharSequence text = (clip.getItemCount() == 0) ? null : clip.getItemAt(0).getText();
1077         if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength) {
1078             clip.getDescription().setClassificationStatus(
1079                     ClipDescription.CLASSIFICATION_NOT_PERFORMED);
1080             return;
1081         }
1082         TextClassifier classifier;
1083         final long ident = Binder.clearCallingIdentity();
1084         try {
1085             classifier = createTextClassificationManagerAsUser(userId)
1086                     .createTextClassificationSession(
1087                             new TextClassificationContext.Builder(
1088                                     getContext().getPackageName(),
1089                                     TextClassifier.WIDGET_TYPE_CLIPBOARD
1090                             ).build()
1091                     );
1092         } finally {
1093             Binder.restoreCallingIdentity(ident);
1094         }
1095         if (text.length() > classifier.getMaxGenerateLinksTextLength()) {
1096             clip.getDescription().setClassificationStatus(
1097                     ClipDescription.CLASSIFICATION_NOT_PERFORMED);
1098             return;
1099         }
1100         mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId, deviceId));
1101     }
1102 
1103     @WorkerThread
doClassification( CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId, int deviceId)1104     private void doClassification(
1105             CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId,
1106             int deviceId) {
1107         TextLinks.Request request = new TextLinks.Request.Builder(text).build();
1108         TextLinks links = classifier.generateLinks(request);
1109 
1110         // Find the highest confidence for each entity in the text.
1111         ArrayMap<String, Float> confidences = new ArrayMap<>();
1112         for (TextLinks.TextLink link : links.getLinks()) {
1113             for (int i = 0; i < link.getEntityCount(); i++) {
1114                 String entity = link.getEntity(i);
1115                 float conf = link.getConfidenceScore(entity);
1116                 if (conf > confidences.getOrDefault(entity, 0f)) {
1117                     confidences.put(entity, conf);
1118                 }
1119             }
1120         }
1121 
1122         synchronized (mLock) {
1123             Clipboard clipboard = getClipboardLocked(userId, deviceId);
1124             if (clipboard == null) {
1125                 return;
1126             }
1127             if (clipboard.primaryClip == clip) {
1128                 applyClassificationAndSendBroadcastLocked(
1129                         clipboard, confidences, links, classifier);
1130 
1131                 // Also apply to related profiles if needed
1132                 List<UserInfo> related = getRelatedProfiles(userId);
1133                 if (related != null) {
1134                     int size = related.size();
1135                     for (int i = 0; i < size; i++) {
1136                         int id = related.get(i).id;
1137                         if (id != userId) {
1138                             final boolean canCopyIntoProfile = !hasRestriction(
1139                                     UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
1140                             if (canCopyIntoProfile) {
1141                                 Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
1142                                 if (relatedClipboard != null
1143                                         && hasTextLocked(relatedClipboard, text)) {
1144                                     applyClassificationAndSendBroadcastLocked(
1145                                             relatedClipboard, confidences, links, classifier);
1146                                 }
1147                             }
1148                         }
1149                     }
1150                 }
1151             }
1152         }
1153     }
1154 
1155     @GuardedBy("mLock")
applyClassificationAndSendBroadcastLocked( Clipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links, TextClassifier classifier)1156     private void applyClassificationAndSendBroadcastLocked(
1157             Clipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,
1158             TextClassifier classifier) {
1159         clipboard.mTextClassifier = classifier;
1160         clipboard.primaryClip.getDescription().setConfidenceScores(confidences);
1161         if (!links.getLinks().isEmpty()) {
1162             clipboard.primaryClip.getItemAt(0).setTextLinks(links);
1163         }
1164         sendClipChangedBroadcast(clipboard);
1165     }
1166 
1167     @GuardedBy("mLock")
hasTextLocked(Clipboard clipboard, @NonNull CharSequence text)1168     private boolean hasTextLocked(Clipboard clipboard, @NonNull CharSequence text) {
1169         return clipboard.primaryClip != null
1170                 && clipboard.primaryClip.getItemCount() > 0
1171                 && text.equals(clipboard.primaryClip.getItemAt(0).getText());
1172     }
1173 
isDeviceLocked(@serIdInt int userId, int deviceId)1174     private boolean isDeviceLocked(@UserIdInt int userId, int deviceId) {
1175         final long token = Binder.clearCallingIdentity();
1176         try {
1177             final KeyguardManager keyguardManager = getContext().getSystemService(
1178                     KeyguardManager.class);
1179             return keyguardManager != null && keyguardManager.isDeviceLocked(userId, deviceId);
1180         } finally {
1181             Binder.restoreCallingIdentity(token);
1182         }
1183     }
1184 
checkUriOwner(Uri uri, int sourceUid)1185     private void checkUriOwner(Uri uri, int sourceUid) {
1186         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1187 
1188         final long ident = Binder.clearCallingIdentity();
1189         try {
1190             // This will throw SecurityException if caller can't grant
1191             mUgmInternal.checkGrantUriPermission(sourceUid, null,
1192                     ContentProvider.getUriWithoutUserId(uri),
1193                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1194                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1195         } finally {
1196             Binder.restoreCallingIdentity(ident);
1197         }
1198     }
1199 
checkItemOwner(ClipData.Item item, int uid)1200     private void checkItemOwner(ClipData.Item item, int uid) {
1201         if (item.getUri() != null) {
1202             checkUriOwner(item.getUri(), uid);
1203         }
1204         Intent intent = item.getIntent();
1205         if (intent != null && intent.getData() != null) {
1206             checkUriOwner(intent.getData(), uid);
1207         }
1208     }
1209 
checkDataOwner(ClipData data, int uid)1210     private void checkDataOwner(ClipData data, int uid) {
1211         final int N = data.getItemCount();
1212         for (int i=0; i<N; i++) {
1213             checkItemOwner(data.getItemAt(i), uid);
1214         }
1215     }
1216 
grantUriPermission(Uri uri, int sourceUid, String targetPkg, int targetUserId)1217     private void grantUriPermission(Uri uri, int sourceUid, String targetPkg,
1218             int targetUserId) {
1219         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1220 
1221         final long ident = Binder.clearCallingIdentity();
1222         try {
1223             mUgm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
1224                     ContentProvider.getUriWithoutUserId(uri),
1225                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1226                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
1227                     targetUserId);
1228         } catch (RemoteException ignored) {
1229             // Ignored because we're in same process
1230         } finally {
1231             Binder.restoreCallingIdentity(ident);
1232         }
1233     }
1234 
grantItemPermission(ClipData.Item item, int sourceUid, String targetPkg, int targetUserId)1235     private void grantItemPermission(ClipData.Item item, int sourceUid, String targetPkg,
1236             int targetUserId) {
1237         if (item.getUri() != null) {
1238             grantUriPermission(item.getUri(), sourceUid, targetPkg, targetUserId);
1239         }
1240         Intent intent = item.getIntent();
1241         if (intent != null && intent.getData() != null) {
1242             grantUriPermission(intent.getData(), sourceUid, targetPkg, targetUserId);
1243         }
1244     }
1245 
1246     @GuardedBy("mLock")
addActiveOwnerLocked(int uid, int deviceId, String pkg)1247     private void addActiveOwnerLocked(int uid, int deviceId, String pkg) {
1248         final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
1249         final int targetUserHandle = UserHandle.getCallingUserId();
1250         final long oldIdentity = Binder.clearCallingIdentity();
1251         try {
1252             if (!pm.isSameApp(pkg, 0, uid, targetUserHandle)) {
1253                 throw new SecurityException("Calling uid " + uid + " does not own package " + pkg);
1254             }
1255         } finally {
1256             Binder.restoreCallingIdentity(oldIdentity);
1257         }
1258         Clipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid), deviceId);
1259         if (clipboard != null && clipboard.primaryClip != null
1260                 && !clipboard.activePermissionOwners.contains(pkg)) {
1261             final int N = clipboard.primaryClip.getItemCount();
1262             for (int i = 0; i < N; i++) {
1263                 grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid,
1264                         pkg, UserHandle.getUserId(uid));
1265             }
1266             clipboard.activePermissionOwners.add(pkg);
1267         }
1268     }
1269 
revokeUriPermission(Uri uri, int sourceUid)1270     private void revokeUriPermission(Uri uri, int sourceUid) {
1271         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
1272 
1273         final long ident = Binder.clearCallingIdentity();
1274         try {
1275             mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner,
1276                     ContentProvider.getUriWithoutUserId(uri),
1277                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
1278                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
1279         } finally {
1280             Binder.restoreCallingIdentity(ident);
1281         }
1282     }
1283 
revokeItemPermission(ClipData.Item item, int sourceUid)1284     private void revokeItemPermission(ClipData.Item item, int sourceUid) {
1285         if (item.getUri() != null) {
1286             revokeUriPermission(item.getUri(), sourceUid);
1287         }
1288         Intent intent = item.getIntent();
1289         if (intent != null && intent.getData() != null) {
1290             revokeUriPermission(intent.getData(), sourceUid);
1291         }
1292     }
1293 
revokeUris(Clipboard clipboard)1294     private void revokeUris(Clipboard clipboard) {
1295         if (clipboard.primaryClip == null) {
1296             return;
1297         }
1298         final int N = clipboard.primaryClip.getItemCount();
1299         for (int i=0; i<N; i++) {
1300             revokeItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);
1301         }
1302     }
1303 
clipboardAccessAllowed( int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId, int intendingDeviceId)1304     private boolean clipboardAccessAllowed(
1305             int op,
1306             String callingPackage,
1307             String attributionTag,
1308             int uid,
1309             @UserIdInt int userId,
1310             int intendingDeviceId) {
1311         return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId,
1312                 intendingDeviceId, true);
1313     }
1314 
clipboardAccessAllowed( int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId, int intendingDeviceId, boolean shouldNoteOp)1315     private boolean clipboardAccessAllowed(
1316             int op,
1317             String callingPackage,
1318             String attributionTag,
1319             int uid,
1320             @UserIdInt int userId,
1321             int intendingDeviceId,
1322             boolean shouldNoteOp) {
1323 
1324         boolean allowed;
1325 
1326         // First, verify package ownership to ensure use below is safe.
1327         mAppOps.checkPackage(uid, callingPackage);
1328 
1329         if (intendingDeviceId == DEVICE_ID_INVALID) {
1330             Slog.w(TAG, "Clipboard access denied to " + uid + "/" + callingPackage
1331                     + " due to invalid device id");
1332             return false;
1333         }
1334 
1335         // Shell can access the clipboard for testing purposes.
1336         if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
1337                     callingPackage) == PackageManager.PERMISSION_GRANTED) {
1338             allowed = true;
1339         } else {
1340             // The default IME is always allowed to access the clipboard.
1341             allowed = isDefaultIme(userId, callingPackage);
1342         }
1343 
1344         switch (op) {
1345             case AppOpsManager.OP_READ_CLIPBOARD:
1346                 // Clipboard can only be read by applications with focus..
1347                 // or the application have the INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL
1348                 // at the same time. e.x. SystemUI. It needs to check the window focus of
1349                 // Binder.getCallingUid(). Without checking, the user X can't copy any thing from
1350                 // INTERNAL_SYSTEM_WINDOW to the other applications.
1351                 if (!allowed) {
1352                     allowed = isDefaultDeviceAndUidFocused(intendingDeviceId, uid)
1353                             || isVirtualDeviceAndUidFocused(intendingDeviceId, uid)
1354                             || isInternalSysWindowAppWithWindowFocus(callingPackage);
1355                 }
1356                 if (!allowed && mContentCaptureInternal != null) {
1357                     // ...or the Content Capture Service
1358                     // The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser
1359                     // is used to check if the uid has the permission BIND_CONTENT_CAPTURE_SERVICE.
1360                     // if the application has the permission, let it to access user's clipboard.
1361                     // To passed synthesized uid user#10_app#systemui may not tell the real uid.
1362                     // userId must pass intending userId. i.e. user#10.
1363                     allowed = mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId);
1364                 }
1365                 if (!allowed && mAutofillInternal != null) {
1366                     // ...or the Augmented Autofill Service
1367                     // The uid parameter of mAutofillInternal.isAugmentedAutofillServiceForUser
1368                     // is used to check if the uid has the permission BIND_AUTOFILL_SERVICE.
1369                     // if the application has the permission, let it to access user's clipboard.
1370                     // To passed synthesized uid user#10_app#systemui may not tell the real uid.
1371                     // userId must pass intending userId. i.e. user#10.
1372                     allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
1373                 }
1374                 if (!allowed && intendingDeviceId != DEVICE_ID_DEFAULT) {
1375                     // Privileged apps which own a VirtualDevice are allowed to read its clipboard
1376                     // in the background.
1377                     allowed = (mVdmInternal != null)
1378                             && mVdmInternal.getDeviceOwnerUid(intendingDeviceId) == uid;
1379                 }
1380                 break;
1381             case AppOpsManager.OP_WRITE_CLIPBOARD:
1382                 // Writing is allowed without focus.
1383                 allowed = true;
1384                 break;
1385             default:
1386                 throw new IllegalArgumentException("Unknown clipboard appop " + op);
1387         }
1388         if (!allowed) {
1389             Slog.e(TAG, "Denying clipboard access to " + callingPackage
1390                     + ", application is not in focus nor is it a system service for "
1391                     + "user " + userId);
1392             return false;
1393         }
1394         // Finally, check the app op.
1395         int appOpsResult;
1396         if (shouldNoteOp) {
1397             appOpsResult = mAppOps.noteOp(op, uid, callingPackage, attributionTag, null);
1398         } else {
1399             appOpsResult = mAppOps.checkOp(op, uid, callingPackage);
1400         }
1401 
1402         return appOpsResult == AppOpsManager.MODE_ALLOWED;
1403     }
1404 
isDefaultDeviceAndUidFocused(int intendingDeviceId, int uid)1405     private boolean isDefaultDeviceAndUidFocused(int intendingDeviceId, int uid) {
1406         return intendingDeviceId == DEVICE_ID_DEFAULT && mWm.isUidFocused(uid);
1407     }
1408 
isVirtualDeviceAndUidFocused(int intendingDeviceId, int uid)1409     private boolean isVirtualDeviceAndUidFocused(int intendingDeviceId, int uid) {
1410         if (intendingDeviceId == DEVICE_ID_DEFAULT || mVdm == null) {
1411             return false;
1412         }
1413         int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
1414         int focusedDeviceId = mVdm.getDeviceIdForDisplayId(topFocusedDisplayId);
1415         return (focusedDeviceId == intendingDeviceId) && mWm.isUidFocused(uid);
1416     }
1417 
isDefaultIme(int userId, String packageName)1418     private boolean isDefaultIme(int userId, String packageName) {
1419         String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
1420                 Settings.Secure.DEFAULT_INPUT_METHOD, userId);
1421         if (!TextUtils.isEmpty(defaultIme)) {
1422             final ComponentName imeComponent = ComponentName.unflattenFromString(defaultIme);
1423             if (imeComponent == null) {
1424                 return false;
1425             }
1426             final String imePkg = imeComponent.getPackageName();
1427             return imePkg.equals(packageName);
1428         }
1429         return false;
1430     }
1431 
1432     /**
1433      * Shows a toast to inform the user that an app has accessed the clipboard. This is only done if
1434      * the setting is enabled, and if the accessing app is not the source of the data and is not the
1435      * IME, the content capture service, or the autofill service. The notification is also only
1436      * shown once per clip for each app.
1437      */
1438     @GuardedBy("mLock")
showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId, Clipboard clipboard)1439     private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
1440             Clipboard clipboard) {
1441         if (clipboard.primaryClip == null) {
1442             return;
1443         }
1444         if (Settings.Secure.getInt(getContext().getContentResolver(),
1445                 Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
1446                 (mShowAccessNotifications ? 1 : 0)) == 0) {
1447             return;
1448         }
1449         // Don't notify if the app accessing the clipboard is the same as the current owner.
1450         if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
1451             return;
1452         }
1453         // Exclude special cases: IME, ContentCapture, Autofill.
1454         if (isDefaultIme(userId, callingPackage)) {
1455             return;
1456         }
1457         if (mContentCaptureInternal != null
1458                 && mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {
1459             return;
1460         }
1461         if (mAutofillInternal != null
1462                 && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
1463             return;
1464         }
1465         if (mPm.checkPermission(Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION,
1466                 callingPackage) == PackageManager.PERMISSION_GRANTED) {
1467             return;
1468         }
1469         // Don't notify if this access is coming from the privileged app which owns the device.
1470         if (clipboard.deviceId != DEVICE_ID_DEFAULT && mVdmInternal.getDeviceOwnerUid(
1471                 clipboard.deviceId) == uid) {
1472             return;
1473         }
1474         // Don't notify if already notified for this uid and clip.
1475         if (clipboard.mNotifiedUids.get(uid)) {
1476             return;
1477         }
1478 
1479         final ArraySet<Context> toastContexts = getToastContexts(clipboard);
1480         Binder.withCleanCallingIdentity(() -> {
1481             try {
1482                 CharSequence callingAppLabel = mPm.getApplicationLabel(
1483                         mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
1484                 String message =
1485                         getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
1486                 Slog.i(TAG, message);
1487                 for (int i = 0; i < toastContexts.size(); i++) {
1488                     Context toastContext = toastContexts.valueAt(i);
1489                     Toast toastToShow;
1490                     if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
1491                         Drawable safetyProtectionIcon = getContext()
1492                                 .getDrawable(R.drawable.ic_safety_protection);
1493                         toastToShow = Toast.makeCustomToastWithIcon(toastContext,
1494                                 UiThread.get().getLooper(), message,
1495                                 Toast.LENGTH_LONG, safetyProtectionIcon);
1496                     } else {
1497                         toastToShow = Toast.makeText(
1498                                 toastContext, UiThread.get().getLooper(), message,
1499                                 Toast.LENGTH_LONG);
1500                     }
1501                     toastToShow.show();
1502                 }
1503             } catch (PackageManager.NameNotFoundException e) {
1504                 // do nothing
1505             }
1506         });
1507 
1508         clipboard.mNotifiedUids.put(uid, true);
1509     }
1510 
1511     /**
1512      * Returns the context(s) to use for toasts related to this clipboard. Normally this will just
1513      * contain a single context referencing the default display.
1514      *
1515      * If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
1516      * the focused VirtualDisplay for that device, but might need to return the contexts for
1517      * multiple displays if the VirtualDevice has several but none of them were focused.
1518      */
getToastContexts(Clipboard clipboard)1519     private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
1520         ArraySet<Context> contexts = new ArraySet<>();
1521 
1522         if (clipboard.deviceId != DEVICE_ID_DEFAULT) {
1523             DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
1524 
1525             int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
1526             ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
1527 
1528             if (displayIds.contains(topFocusedDisplayId)) {
1529                 Display display = displayManager.getDisplay(topFocusedDisplayId);
1530                 if (display != null) {
1531                     contexts.add(getContext().createDisplayContext(display));
1532                     return contexts;
1533                 }
1534             }
1535 
1536             for (int i = 0; i < displayIds.size(); i++) {
1537                 Display display = displayManager.getDisplay(displayIds.valueAt(i));
1538                 if (display != null) {
1539                     contexts.add(getContext().createDisplayContext(display));
1540                 }
1541             }
1542             if (!contexts.isEmpty()) {
1543                 return contexts;
1544             }
1545             Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
1546                     + clipboard.deviceId);
1547             // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
1548             // the default display below.
1549         }
1550 
1551         contexts.add(getContext());
1552         return contexts;
1553     }
1554 
1555     /**
1556      * Returns true if the provided {@link ClipData} represents a single piece of text. That is, if
1557      * there is only on {@link ClipData.Item}, and that item contains a non-empty piece of text and
1558      * no URI or Intent. Note that HTML may be provided along with text so the presence of
1559      * HtmlText in the clip does not prevent this method returning true.
1560      */
isText(@onNull ClipData data)1561     private static boolean isText(@NonNull ClipData data) {
1562         if (data.getItemCount() > 1) {
1563             return false;
1564         }
1565         ClipData.Item item = data.getItemAt(0);
1566 
1567         return !TextUtils.isEmpty(item.getText()) && item.getUri() == null
1568                 && item.getIntent() == null;
1569     }
1570 
1571     /** Potentially notifies the text classifier that an app is accessing a text clip. */
1572     @GuardedBy("mLock")
notifyTextClassifierLocked( Clipboard clipboard, String callingPackage, int callingUid)1573     private void notifyTextClassifierLocked(
1574             Clipboard clipboard, String callingPackage, int callingUid) {
1575         if (clipboard.primaryClip == null) {
1576             return;
1577         }
1578         ClipData.Item item = clipboard.primaryClip.getItemAt(0);
1579         if (item == null) {
1580             return;
1581         }
1582         if (!isText(clipboard.primaryClip)) {
1583             return;
1584         }
1585         TextClassifier textClassifier = clipboard.mTextClassifier;
1586         // Don't notify text classifier if we haven't used it to annotate the text in the clip.
1587         if (textClassifier == null) {
1588             return;
1589         }
1590         // Don't notify text classifier if the app reading the clipboard does not have the focus.
1591         if (!mWm.isUidFocused(callingUid)) {
1592             return;
1593         }
1594         // Don't notify text classifier again if already notified for this uid and clip.
1595         if (clipboard.mNotifiedTextClassifierUids.get(callingUid)) {
1596             return;
1597         }
1598         clipboard.mNotifiedTextClassifierUids.put(callingUid, true);
1599         Binder.withCleanCallingIdentity(() -> {
1600             TextClassifierEvent.TextLinkifyEvent pasteEvent =
1601                     new TextClassifierEvent.TextLinkifyEvent.Builder(
1602                             TextClassifierEvent.TYPE_READ_CLIPBOARD)
1603                             .setEventContext(new TextClassificationContext.Builder(
1604                                     callingPackage, TextClassifier.WIDGET_TYPE_CLIPBOARD)
1605                                     .build())
1606                             .setExtras(
1607                                     Bundle.forPair("source_package", clipboard.mPrimaryClipPackage))
1608                             .build();
1609             textClassifier.onTextClassifierEvent(pasteEvent);
1610         });
1611     }
1612 
createTextClassificationManagerAsUser(@serIdInt int userId)1613     private TextClassificationManager createTextClassificationManagerAsUser(@UserIdInt int userId) {
1614         Context context = getContext().createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
1615         return context.getSystemService(TextClassificationManager.class);
1616     }
1617 }
1618