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