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