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