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