Android12Rom定制去掉剪贴板复制成功的Toast提示

1.前言:

最近在rom定制化开发时,测试提了一个bug,在浏览器或者文本里面使用剪贴板复制成功后会有一个Toast提示,这种体验不是很好,因为每次复制成功都有一个提示,感觉很突兀,修改很简单,去掉toast即可,所以这里记录一下过程.

2.问题现象截图如下:

在这里插入图片描述

3.日志分析:

通过logcat日志分析发现在剪切板服务类中有打印toast提示的内容,问题复现了,接下来就好办多了,根据日志找到剪切板服务类.

在这里插入图片描述

4.源码路径:

frameworks/base/services/core/com/android/server/ClipboardService

/** Copyright (C) 2008 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.server.clipboard;import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUriGrantsManager;
import android.app.KeyguardManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.autofill.AutofillManagerInternal;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLinks;
import android.widget.Toast;import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;import java.util.HashSet;
import java.util.List;
import java.util.function.Consumer;/*** Implementation of the clipboard for copy and paste.* <p>* Caution: exception for clipboard data and isInternalSysWindowAppWithWindowFocus, any of data* is accessed by userId or uid should be in * the try segment between* Binder.clearCallingIdentity and Binder.restoreCallingIdentity.* </p>*/
public class ClipboardService extends SystemService {private static final String TAG = "ClipboardService";private static final boolean IS_EMULATOR =SystemProperties.getBoolean("ro.boot.qemu", false);// DeviceConfig propertiesprivate static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;private final ActivityManagerInternal mAmInternal;private final IUriGrantsManager mUgm;private final UriGrantsManagerInternal mUgmInternal;private final WindowManagerInternal mWm;private final IUserManager mUm;private final PackageManager mPm;private final AppOpsManager mAppOps;private final ContentCaptureManagerInternal mContentCaptureInternal;private final AutofillManagerInternal mAutofillInternal;private final IBinder mPermissionOwner;private final Consumer<ClipData> mEmulatorClipboardMonitor;private final Handler mWorkerHandler;@GuardedBy("mLock")private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();@GuardedBy("mLock")private boolean mShowAccessNotifications =ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;@GuardedBy("mLock")private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;private final Object mLock = new Object();/*** Instantiates the clipboard.*/public ClipboardService(Context context) {super(context);mAmInternal = LocalServices.getService(ActivityManagerInternal.class);mUgm = UriGrantsManager.getService();mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);mWm = LocalServices.getService(WindowManagerInternal.class);mPm = getContext().getPackageManager();mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");mPermissionOwner = permOwner;if (IS_EMULATOR) {mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {synchronized (mLock) {setPrimaryClipInternalLocked(getClipboardLocked(0), clip,android.os.Process.SYSTEM_UID, null);}});} else {mEmulatorClipboardMonitor = (clip) -> {};}updateConfig();DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,getContext().getMainExecutor(), properties -> updateConfig());HandlerThread workerThread = new HandlerThread(TAG);workerThread.start();mWorkerHandler = workerThread.getThreadHandler();}@Overridepublic void onStart() {publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());}@Overridepublic void onUserStopped(@NonNull TargetUser user) {synchronized (mLock) {mClipboards.remove(user.getUserIdentifier());}}private void updateConfig() {synchronized (mLock) {mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);}}private class ListenerInfo {final int mUid;final String mPackageName;ListenerInfo(int uid, String packageName) {mUid = uid;mPackageName = packageName;}}private class PerUserClipboard {final int userId;final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners= new RemoteCallbackList<IOnPrimaryClipChangedListener>();/** Current primary clip. */ClipData primaryClip;/** UID that set {@link #primaryClip}. */int primaryClipUid = android.os.Process.NOBODY_UID;/** Package of the app that set {@link #primaryClip}. */String mPrimaryClipPackage;/** Uids that have already triggered a toast notification for {@link #primaryClip} */final SparseBooleanArray mNotifiedUids = new SparseBooleanArray();/*** Uids that have already triggered a notification to text classifier for* {@link #primaryClip}.*/final SparseBooleanArray mNotifiedTextClassifierUids = new SparseBooleanArray();final HashSet<String> activePermissionOwners= new HashSet<String>();/** The text classifier session that is used to annotate the text in the primary clip. */TextClassifier mTextClassifier;PerUserClipboard(int userId) {this.userId = userId;}}/*** To check if the application has granted the INTERNAL_SYSTEM_WINDOW permission and window* focus.* <p>* All of applications granted INTERNAL_SYSTEM_WINDOW has the risk to leak clip information to* the other user because INTERNAL_SYSTEM_WINDOW is signature level. i.e. platform key. Because* some of applications have both of INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL at* the same time, that means they show the same window to all of users.* </p><p>* Unfortunately, all of applications with INTERNAL_SYSTEM_WINDOW starts very early and then* the real window show is belong to user 0 rather user X. The result of* WindowManager.isUidFocused checking user X window is false.* </p>* @return true if the app granted INTERNAL_SYSTEM_WINDOW permission.*/private boolean isInternalSysWindowAppWithWindowFocus(String callingPackage) {// Shell can access the clipboard for testing purposes.if (mPm.checkPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,callingPackage) == PackageManager.PERMISSION_GRANTED) {if (mWm.isUidFocused(Binder.getCallingUid())) {return true;}}return false;}/*** To get the validate current userId.* <p>* The intending userId needs to be validated by ActivityManagerInternal.handleIncomingUser.* To check if the uid of the process have the permission to run as the userId.* e.x. INTERACT_ACROSS_USERS_FULL or INTERACT_ACROSS_USERS permission granted.* </p>* <p>* The application with the granted INTERNAL_SYSTEM_WINDOW permission should run as the output* of ActivityManagerInternal.handleIncomingUser rather the userId of Binder.getCAllingUid().* To use the userId of Binder.getCallingUid() is the root cause that leaks the information* comes from user 0 to user X.* </p>** @param packageName the package name of the calling side* @param userId the userId passed by the calling side* @return return the intending userId that has been validated by ActivityManagerInternal.*/@UserIdIntprivate int getIntendingUserId(String packageName, @UserIdInt int userId) {final int callingUid = Binder.getCallingUid();final int callingUserId = UserHandle.getUserId(callingUid);if (!UserManager.supportsMultipleUsers() || callingUserId == userId) {return callingUserId;}int intendingUserId = callingUserId;intendingUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),Binder.getCallingUid(), userId, false /* allow all */, ALLOW_FULL_ONLY,"checkClipboardServiceCallingUser", packageName);return intendingUserId;}/*** To get the current running uid who is intend to run as.* In ording to distinguish the nameing and reducing the confusing names, the client client* side pass userId that is intend to run as,* @return return IntentingUid = validated intenting userId +*         UserHandle.getAppId(Binder.getCallingUid())*/private int getIntendingUid(String packageName, @UserIdInt int userId) {return UserHandle.getUid(getIntendingUserId(packageName, userId),UserHandle.getAppId(Binder.getCallingUid()));}/*** To handle the difference between userId and intendingUserId, uid and intendingUid.** userId means that comes from the calling side and should be validated by* ActivityManagerInternal.handleIncomingUser.* After validation of ActivityManagerInternal.handleIncomingUser, the userId is called* 'intendingUserId' and the uid is called 'intendingUid'.*/private class ClipboardImpl extends IClipboard.Stub {@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {try {return super.onTransact(code, data, reply, flags);} catch (RuntimeException e) {if (!(e instanceof SecurityException)) {Slog.wtf("clipboard", "Exception: ", e);}throw e;}}@Overridepublic void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) {checkAndSetPrimaryClip(clip, callingPackage, userId, callingPackage);}@Overridepublic void setPrimaryClipAsPackage(ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,"Requires SET_CLIP_SOURCE permission");checkAndSetPrimaryClip(clip, callingPackage, userId, sourcePackage);}private void checkAndSetPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {if (clip == null || clip.getItemCount() <= 0) {throw new IllegalArgumentException("No items");}final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,intendingUid, intendingUserId)) {return;}checkDataOwner(clip, intendingUid);synchronized (mLock) {setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);}}@Overridepublic void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,intendingUid, intendingUserId)) {return;}synchronized (mLock) {setPrimaryClipInternalLocked(null, intendingUid, callingPackage);}}@Overridepublic ClipData getPrimaryClip(String pkg, @UserIdInt int userId) {final int intendingUid = getIntendingUid(pkg, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,intendingUid, intendingUserId)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {try {addActiveOwnerLocked(intendingUid, pkg);} catch (SecurityException e) {// Permission could not be granted - URI may be invalidSlog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");setPrimaryClipInternalLocked(null, intendingUid, pkg);return null;}PerUserClipboard clipboard = getClipboardLocked(intendingUserId);showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);notifyTextClassifierLocked(clipboard, pkg, intendingUid);return clipboard.primaryClip;}}@Overridepublic ClipDescription getPrimaryClipDescription(String callingPackage,@UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);return clipboard.primaryClip != null? clipboard.primaryClip.getDescription() : null;}}@Overridepublic boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return false;}synchronized (mLock) {return getClipboardLocked(intendingUserId).primaryClip != null;}}@Overridepublic void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);synchronized (mLock) {getClipboardLocked(intendingUserId).primaryClipListeners.register(listener,new ListenerInfo(intendingUid, callingPackage));}}@Overridepublic void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,String callingPackage, @UserIdInt int userId) {final int intendingUserId = getIntendingUserId(callingPackage, userId);synchronized (mLock) {getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);}}@Overridepublic boolean hasClipboardText(String callingPackage, int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return false;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);if (clipboard.primaryClip != null) {CharSequence text = clipboard.primaryClip.getItemAt(0).getText();return text != null && text.length() > 0;}return false;}}@Overridepublic String getPrimaryClipSource(String callingPackage, int userId) {getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,"Requires SET_CLIP_SOURCE permission");final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);if (clipboard.primaryClip != null) {return clipboard.mPrimaryClipPackage;}return null;}}};@GuardedBy("mLock")private PerUserClipboard getClipboardLocked(@UserIdInt int userId) {PerUserClipboard puc = mClipboards.get(userId);if (puc == null) {puc = new PerUserClipboard(userId);mClipboards.put(userId, puc);}return puc;}List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {final List<UserInfo> related;final long origId = Binder.clearCallingIdentity();try {related = mUm.getProfiles(userId, true);} catch (RemoteException e) {Slog.e(TAG, "Remote Exception calling UserManager: " + e);return null;} finally{Binder.restoreCallingIdentity(origId);}return related;}/** Check if the user has the given restriction set. Default to true if error occured during* calling UserManager, so it fails safe.*/private boolean hasRestriction(String restriction, int userId) {try {return mUm.hasUserRestriction(restriction, userId);} catch (RemoteException e) {Slog.e(TAG, "Remote Exception calling UserManager.getUserRestrictions: ", e);// Fails safereturn true;}}void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {synchronized (mLock) {setPrimaryClipInternalLocked(clip, uid, null);}}@GuardedBy("mLock")private void setPrimaryClipInternalLocked(@Nullable ClipData clip, int uid, @Nullable String sourcePackage) {mEmulatorClipboardMonitor.accept(clip);final int userId = UserHandle.getUserId(uid);if (clip != null) {startClassificationLocked(clip, userId);}// Update this usersetPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);// Update related usersList<UserInfo> related = getRelatedProfiles(userId);if (related != null) {int size = related.size();if (size > 1) { // Related profiles list include the current profile.final boolean canCopy = !hasRestriction(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, userId);// Copy clip data to related users if allowed. If disallowed, then remove// primary clip in related users to prevent pasting stale content.if (!canCopy) {clip = null;} else if (clip == null) {// do nothing for canCopy == true and clip == null case// To prevent from NPE happen in 'new ClipData(clip)' when run// android.content.cts.ClipboardManagerTest#testClearPrimaryClip} else {// We want to fix the uris of the related user's clip without changing the// uris of the current user's clip.// So, copy the ClipData, and then copy all the items, so that nothing// is shared in memory.clip = new ClipData(clip);for (int i = clip.getItemCount() - 1; i >= 0; i--) {clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));}clip.fixUrisLight(userId);}for (int i = 0; i < size; i++) {int id = related.get(i).id;if (id != userId) {final boolean canCopyIntoProfile = !hasRestriction(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);if (canCopyIntoProfile) {setPrimaryClipInternalLocked(getClipboardLocked(id), clip, uid, sourcePackage);}}}}}}void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,int uid) {synchronized ("mLock") {setPrimaryClipInternalLocked(clipboard, clip, uid, null);}}@GuardedBy("mLock")private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,int uid, @Nullable String sourcePackage) {revokeUris(clipboard);clipboard.activePermissionOwners.clear();if (clip == null && clipboard.primaryClip == null) {return;}clipboard.primaryClip = clip;clipboard.mNotifiedUids.clear();clipboard.mNotifiedTextClassifierUids.clear();if (clip != null) {clipboard.primaryClipUid = uid;clipboard.mPrimaryClipPackage = sourcePackage;} else {clipboard.primaryClipUid = android.os.Process.NOBODY_UID;clipboard.mPrimaryClipPackage = null;}if (clip != null) {final ClipDescription description = clip.getDescription();if (description != null) {description.setTimestamp(System.currentTimeMillis());}}sendClipChangedBroadcast(clipboard);}private void sendClipChangedBroadcast(PerUserClipboard clipboard) {final long ident = Binder.clearCallingIdentity();final int n = clipboard.primaryClipListeners.beginBroadcast();try {for (int i = 0; i < n; i++) {try {ListenerInfo li = (ListenerInfo)clipboard.primaryClipListeners.getBroadcastCookie(i);if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName,li.mUid, UserHandle.getUserId(li.mUid))) {clipboard.primaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();}} catch (RemoteException e) {// The RemoteCallbackList will take care of removing// the dead object for us.}}} finally {clipboard.primaryClipListeners.finishBroadcast();Binder.restoreCallingIdentity(ident);}}@GuardedBy("mLock")private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) {CharSequence text = (clip.getItemCount() == 0) ? null : clip.getItemAt(0).getText();if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength) {clip.getDescription().setClassificationStatus(ClipDescription.CLASSIFICATION_NOT_PERFORMED);return;}TextClassifier classifier;final long ident = Binder.clearCallingIdentity();try {classifier = createTextClassificationManagerAsUser(userId).createTextClassificationSession(new TextClassificationContext.Builder(getContext().getPackageName(),TextClassifier.WIDGET_TYPE_CLIPBOARD).build());} finally {Binder.restoreCallingIdentity(ident);}if (text.length() > classifier.getMaxGenerateLinksTextLength()) {clip.getDescription().setClassificationStatus(ClipDescription.CLASSIFICATION_NOT_PERFORMED);return;}mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId));}@WorkerThreadprivate void doClassification(CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId) {TextLinks.Request request = new TextLinks.Request.Builder(text).build();TextLinks links = classifier.generateLinks(request);// Find the highest confidence for each entity in the text.ArrayMap<String, Float> confidences = new ArrayMap<>();for (TextLinks.TextLink link : links.getLinks()) {for (int i = 0; i < link.getEntityCount(); i++) {String entity = link.getEntity(i);float conf = link.getConfidenceScore(entity);if (conf > confidences.getOrDefault(entity, 0f)) {confidences.put(entity, conf);}}}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(userId);if (clipboard.primaryClip == clip) {applyClassificationAndSendBroadcastLocked(clipboard, confidences, links, classifier);// Also apply to related profiles if neededList<UserInfo> related = getRelatedProfiles(userId);if (related != null) {int size = related.size();for (int i = 0; i < size; i++) {int id = related.get(i).id;if (id != userId) {final boolean canCopyIntoProfile = !hasRestriction(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);if (canCopyIntoProfile) {PerUserClipboard relatedClipboard = getClipboardLocked(id);if (hasTextLocked(relatedClipboard, text)) {applyClassificationAndSendBroadcastLocked(relatedClipboard, confidences, links, classifier);}}}}}}}}@GuardedBy("mLock")private void applyClassificationAndSendBroadcastLocked(PerUserClipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,TextClassifier classifier) {clipboard.mTextClassifier = classifier;clipboard.primaryClip.getDescription().setConfidenceScores(confidences);if (!links.getLinks().isEmpty()) {clipboard.primaryClip.getItemAt(0).setTextLinks(links);}sendClipChangedBroadcast(clipboard);}@GuardedBy("mLock")private boolean hasTextLocked(PerUserClipboard clipboard, @NonNull CharSequence text) {return clipboard.primaryClip != null&& clipboard.primaryClip.getItemCount() > 0&& text.equals(clipboard.primaryClip.getItemAt(0).getText());}private boolean isDeviceLocked(@UserIdInt int userId) {final long token = Binder.clearCallingIdentity();try {final KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class);return keyguardManager != null && keyguardManager.isDeviceLocked(userId);} finally {Binder.restoreCallingIdentity(token);}}private void checkUriOwner(Uri uri, int sourceUid) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {// This will throw SecurityException if caller can't grantmUgmInternal.checkGrantUriPermission(sourceUid, null,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));} finally {Binder.restoreCallingIdentity(ident);}}private void checkItemOwner(ClipData.Item item, int uid) {if (item.getUri() != null) {checkUriOwner(item.getUri(), uid);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {checkUriOwner(intent.getData(), uid);}}private void checkDataOwner(ClipData data, int uid) {final int N = data.getItemCount();for (int i=0; i<N; i++) {checkItemOwner(data.getItemAt(i), uid);}}private void grantUriPermission(Uri uri, int sourceUid, String targetPkg,int targetUserId) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {mUgm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),targetUserId);} catch (RemoteException ignored) {// Ignored because we're in same process} finally {Binder.restoreCallingIdentity(ident);}}private void grantItemPermission(ClipData.Item item, int sourceUid, String targetPkg,int targetUserId) {if (item.getUri() != null) {grantUriPermission(item.getUri(), sourceUid, targetPkg, targetUserId);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {grantUriPermission(intent.getData(), sourceUid, targetPkg, targetUserId);}}@GuardedBy("mLock")private void addActiveOwnerLocked(int uid, String pkg) {final IPackageManager pm = AppGlobals.getPackageManager();final int targetUserHandle = UserHandle.getCallingUserId();final long oldIdentity = Binder.clearCallingIdentity();try {PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle);if (pi == null) {throw new IllegalArgumentException("Unknown package " + pkg);}if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {throw new SecurityException("Calling uid " + uid+ " does not own package " + pkg);}} catch (RemoteException e) {// Can't happen; the package manager is in the same process} finally {Binder.restoreCallingIdentity(oldIdentity);}PerUserClipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid));if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {final int N = clipboard.primaryClip.getItemCount();for (int i=0; i<N; i++) {grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid,pkg, UserHandle.getUserId(uid));}clipboard.activePermissionOwners.add(pkg);}}private void revokeUriPermission(Uri uri, int sourceUid) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));} finally {Binder.restoreCallingIdentity(ident);}}private void revokeItemPermission(ClipData.Item item, int sourceUid) {if (item.getUri() != null) {revokeUriPermission(item.getUri(), sourceUid);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {revokeUriPermission(intent.getData(), sourceUid);}}private void revokeUris(PerUserClipboard clipboard) {if (clipboard.primaryClip == null) {return;}final int N = clipboard.primaryClip.getItemCount();for (int i=0; i<N; i++) {revokeItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);}}private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,@UserIdInt int userId) {return clipboardAccessAllowed(op, callingPackage, uid, userId, true);}private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,@UserIdInt int userId, boolean shouldNoteOp) {boolean allowed;// First, verify package ownership to ensure use below is safe.mAppOps.checkPackage(uid, callingPackage);// Shell can access the clipboard for testing purposes.if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,callingPackage) == PackageManager.PERMISSION_GRANTED) {allowed = true;} else {// The default IME is always allowed to access the clipboard.allowed = isDefaultIme(userId, callingPackage);}switch (op) {case AppOpsManager.OP_READ_CLIPBOARD:// Clipboard can only be read by applications with focus..// or the application have the INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL// at the same time. e.x. SystemUI. It needs to check the window focus of// Binder.getCallingUid(). Without checking, the user X can't copy any thing from// INTERNAL_SYSTEM_WINDOW to the other applications.if (!allowed) {allowed = mWm.isUidFocused(uid)|| isInternalSysWindowAppWithWindowFocus(callingPackage);}if (!allowed && mContentCaptureInternal != null) {// ...or the Content Capture Service// The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser// is used to check if the uid has the permission BIND_CONTENT_CAPTURE_SERVICE.// if the application has the permission, let it to access user's clipboard.// To passed synthesized uid user#10_app#systemui may not tell the real uid.// userId must pass intending userId. i.e. user#10.allowed = mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId);}if (!allowed && mAutofillInternal != null) {// ...or the Augmented Autofill Service// The uid parameter of mAutofillInternal.isAugmentedAutofillServiceForUser// is used to check if the uid has the permission BIND_AUTOFILL_SERVICE.// if the application has the permission, let it to access user's clipboard.// To passed synthesized uid user#10_app#systemui may not tell the real uid.// userId must pass intending userId. i.e. user#10.allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);}break;case AppOpsManager.OP_WRITE_CLIPBOARD:// Writing is allowed without focus.allowed = true;break;default:throw new IllegalArgumentException("Unknown clipboard appop " + op);}if (!allowed) {Slog.e(TAG, "Denying clipboard access to " + callingPackage+ ", application is not in focus nor is it a system service for "+ "user " + userId);return false;}// Finally, check the app op.int appOpsResult;if (shouldNoteOp) {appOpsResult = mAppOps.noteOp(op, uid, callingPackage);} else {appOpsResult = mAppOps.checkOp(op, uid, callingPackage);}return appOpsResult == AppOpsManager.MODE_ALLOWED;}private boolean isDefaultIme(int userId, String packageName) {String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),Settings.Secure.DEFAULT_INPUT_METHOD, userId);if (!TextUtils.isEmpty(defaultIme)) {final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();return imePkg.equals(packageName);}return false;}/*** Shows a toast to inform the user that an app has accessed the clipboard. This is only done if* the setting is enabled, and if the accessing app is not the source of the data and is not the* IME, the content capture service, or the autofill service. The notification is also only* shown once per clip for each app.*/@GuardedBy("mLock")private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,PerUserClipboard clipboard) {if (clipboard.primaryClip == null) {return;}if (Settings.Secure.getInt(getContext().getContentResolver(),Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,(mShowAccessNotifications ? 1 : 0)) == 0) {return;}// Don't notify if the app accessing the clipboard is the same as the current owner.if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {return;}// Exclude special cases: IME, ContentCapture, Autofill.if (isDefaultIme(userId, callingPackage)) {return;}if (mContentCaptureInternal != null&& mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {return;}if (mAutofillInternal != null&& mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {return;}// Don't notify if already notified for this uid and clip.if (clipboard.mNotifiedUids.get(uid)) {return;}clipboard.mNotifiedUids.put(uid, true);Binder.withCleanCallingIdentity(() -> {try {CharSequence callingAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(callingPackage, 0, userId));String message =getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);Slog.i(TAG, message);Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT).show(); } catch (PackageManager.NameNotFoundException e) {// do nothing}});}/*** Returns true if the provided {@link ClipData} represents a single piece of text. That is, if* there is only on {@link ClipData.Item}, and that item contains a non-empty piece of text and* no URI or Intent. Note that HTML may be provided along with text so the presence of* HtmlText in the clip does not prevent this method returning true.*/private static boolean isText(@NonNull ClipData data) {if (data.getItemCount() > 1) {return false;}ClipData.Item item = data.getItemAt(0);return !TextUtils.isEmpty(item.getText()) && item.getUri() == null&& item.getIntent() == null;}/** Potentially notifies the text classifier that an app is accessing a text clip. */@GuardedBy("mLock")private void notifyTextClassifierLocked(PerUserClipboard clipboard, String callingPackage, int callingUid) {if (clipboard.primaryClip == null) {return;}ClipData.Item item = clipboard.primaryClip.getItemAt(0);if (item == null) {return;}if (!isText(clipboard.primaryClip)) {return;}TextClassifier textClassifier = clipboard.mTextClassifier;// Don't notify text classifier if we haven't used it to annotate the text in the clip.if (textClassifier == null) {return;}// Don't notify text classifier if the app reading the clipboard does not have the focus.if (!mWm.isUidFocused(callingUid)) {return;}// Don't notify text classifier again if already notified for this uid and clip.if (clipboard.mNotifiedTextClassifierUids.get(callingUid)) {return;}clipboard.mNotifiedTextClassifierUids.put(callingUid, true);Binder.withCleanCallingIdentity(() -> {TextClassifierEvent.TextLinkifyEvent pasteEvent =new TextClassifierEvent.TextLinkifyEvent.Builder(TextClassifierEvent.TYPE_READ_CLIPBOARD).setEventContext(new TextClassificationContext.Builder(callingPackage, TextClassifier.WIDGET_TYPE_CLIPBOARD).build()).setExtras(Bundle.forPair("source_package", clipboard.mPrimaryClipPackage)).build();textClassifier.onTextClassifierEvent(pasteEvent);});}private TextClassificationManager createTextClassificationManagerAsUser(@UserIdInt int userId) {Context context = getContext().createContextAsUser(UserHandle.of(userId), /* flags= */ 0);return context.getSystemService(TextClassificationManager.class);}
}

5.解决方法:

关键修改点:屏蔽或者删除toast提示

  Binder.withCleanCallingIdentity(() -> {try {CharSequence callingAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(callingPackage, 0, userId));String message =getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);Slog.i(TAG, message);/* Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT).show(); */} catch (PackageManager.NameNotFoundException e) {// do nothing}});
/** Copyright (C) 2008 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.server.clipboard;import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUriGrantsManager;
import android.app.KeyguardManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.autofill.AutofillManagerInternal;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextClassifierEvent;
import android.view.textclassifier.TextLinks;
import android.widget.Toast;import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;import java.util.HashSet;
import java.util.List;
import java.util.function.Consumer;/*** Implementation of the clipboard for copy and paste.* <p>* Caution: exception for clipboard data and isInternalSysWindowAppWithWindowFocus, any of data* is accessed by userId or uid should be in * the try segment between* Binder.clearCallingIdentity and Binder.restoreCallingIdentity.* </p>*/
public class ClipboardService extends SystemService {private static final String TAG = "ClipboardService";private static final boolean IS_EMULATOR =SystemProperties.getBoolean("ro.boot.qemu", false);// DeviceConfig propertiesprivate static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;private final ActivityManagerInternal mAmInternal;private final IUriGrantsManager mUgm;private final UriGrantsManagerInternal mUgmInternal;private final WindowManagerInternal mWm;private final IUserManager mUm;private final PackageManager mPm;private final AppOpsManager mAppOps;private final ContentCaptureManagerInternal mContentCaptureInternal;private final AutofillManagerInternal mAutofillInternal;private final IBinder mPermissionOwner;private final Consumer<ClipData> mEmulatorClipboardMonitor;private final Handler mWorkerHandler;@GuardedBy("mLock")private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();@GuardedBy("mLock")private boolean mShowAccessNotifications =ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;@GuardedBy("mLock")private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;private final Object mLock = new Object();/*** Instantiates the clipboard.*/public ClipboardService(Context context) {super(context);mAmInternal = LocalServices.getService(ActivityManagerInternal.class);mUgm = UriGrantsManager.getService();mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);mWm = LocalServices.getService(WindowManagerInternal.class);mPm = getContext().getPackageManager();mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class);mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");mPermissionOwner = permOwner;if (IS_EMULATOR) {mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {synchronized (mLock) {setPrimaryClipInternalLocked(getClipboardLocked(0), clip,android.os.Process.SYSTEM_UID, null);}});} else {mEmulatorClipboardMonitor = (clip) -> {};}updateConfig();DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CLIPBOARD,getContext().getMainExecutor(), properties -> updateConfig());HandlerThread workerThread = new HandlerThread(TAG);workerThread.start();mWorkerHandler = workerThread.getThreadHandler();}@Overridepublic void onStart() {publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());}@Overridepublic void onUserStopped(@NonNull TargetUser user) {synchronized (mLock) {mClipboards.remove(user.getUserIdentifier());}}private void updateConfig() {synchronized (mLock) {mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);}}private class ListenerInfo {final int mUid;final String mPackageName;ListenerInfo(int uid, String packageName) {mUid = uid;mPackageName = packageName;}}private class PerUserClipboard {final int userId;final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners= new RemoteCallbackList<IOnPrimaryClipChangedListener>();/** Current primary clip. */ClipData primaryClip;/** UID that set {@link #primaryClip}. */int primaryClipUid = android.os.Process.NOBODY_UID;/** Package of the app that set {@link #primaryClip}. */String mPrimaryClipPackage;/** Uids that have already triggered a toast notification for {@link #primaryClip} */final SparseBooleanArray mNotifiedUids = new SparseBooleanArray();/*** Uids that have already triggered a notification to text classifier for* {@link #primaryClip}.*/final SparseBooleanArray mNotifiedTextClassifierUids = new SparseBooleanArray();final HashSet<String> activePermissionOwners= new HashSet<String>();/** The text classifier session that is used to annotate the text in the primary clip. */TextClassifier mTextClassifier;PerUserClipboard(int userId) {this.userId = userId;}}/*** To check if the application has granted the INTERNAL_SYSTEM_WINDOW permission and window* focus.* <p>* All of applications granted INTERNAL_SYSTEM_WINDOW has the risk to leak clip information to* the other user because INTERNAL_SYSTEM_WINDOW is signature level. i.e. platform key. Because* some of applications have both of INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL at* the same time, that means they show the same window to all of users.* </p><p>* Unfortunately, all of applications with INTERNAL_SYSTEM_WINDOW starts very early and then* the real window show is belong to user 0 rather user X. The result of* WindowManager.isUidFocused checking user X window is false.* </p>* @return true if the app granted INTERNAL_SYSTEM_WINDOW permission.*/private boolean isInternalSysWindowAppWithWindowFocus(String callingPackage) {// Shell can access the clipboard for testing purposes.if (mPm.checkPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,callingPackage) == PackageManager.PERMISSION_GRANTED) {if (mWm.isUidFocused(Binder.getCallingUid())) {return true;}}return false;}/*** To get the validate current userId.* <p>* The intending userId needs to be validated by ActivityManagerInternal.handleIncomingUser.* To check if the uid of the process have the permission to run as the userId.* e.x. INTERACT_ACROSS_USERS_FULL or INTERACT_ACROSS_USERS permission granted.* </p>* <p>* The application with the granted INTERNAL_SYSTEM_WINDOW permission should run as the output* of ActivityManagerInternal.handleIncomingUser rather the userId of Binder.getCAllingUid().* To use the userId of Binder.getCallingUid() is the root cause that leaks the information* comes from user 0 to user X.* </p>** @param packageName the package name of the calling side* @param userId the userId passed by the calling side* @return return the intending userId that has been validated by ActivityManagerInternal.*/@UserIdIntprivate int getIntendingUserId(String packageName, @UserIdInt int userId) {final int callingUid = Binder.getCallingUid();final int callingUserId = UserHandle.getUserId(callingUid);if (!UserManager.supportsMultipleUsers() || callingUserId == userId) {return callingUserId;}int intendingUserId = callingUserId;intendingUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),Binder.getCallingUid(), userId, false /* allow all */, ALLOW_FULL_ONLY,"checkClipboardServiceCallingUser", packageName);return intendingUserId;}/*** To get the current running uid who is intend to run as.* In ording to distinguish the nameing and reducing the confusing names, the client client* side pass userId that is intend to run as,* @return return IntentingUid = validated intenting userId +*         UserHandle.getAppId(Binder.getCallingUid())*/private int getIntendingUid(String packageName, @UserIdInt int userId) {return UserHandle.getUid(getIntendingUserId(packageName, userId),UserHandle.getAppId(Binder.getCallingUid()));}/*** To handle the difference between userId and intendingUserId, uid and intendingUid.** userId means that comes from the calling side and should be validated by* ActivityManagerInternal.handleIncomingUser.* After validation of ActivityManagerInternal.handleIncomingUser, the userId is called* 'intendingUserId' and the uid is called 'intendingUid'.*/private class ClipboardImpl extends IClipboard.Stub {@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {try {return super.onTransact(code, data, reply, flags);} catch (RuntimeException e) {if (!(e instanceof SecurityException)) {Slog.wtf("clipboard", "Exception: ", e);}throw e;}}@Overridepublic void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) {checkAndSetPrimaryClip(clip, callingPackage, userId, callingPackage);}@Overridepublic void setPrimaryClipAsPackage(ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,"Requires SET_CLIP_SOURCE permission");checkAndSetPrimaryClip(clip, callingPackage, userId, sourcePackage);}private void checkAndSetPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {if (clip == null || clip.getItemCount() <= 0) {throw new IllegalArgumentException("No items");}final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,intendingUid, intendingUserId)) {return;}checkDataOwner(clip, intendingUid);synchronized (mLock) {setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);}}@Overridepublic void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,intendingUid, intendingUserId)) {return;}synchronized (mLock) {setPrimaryClipInternalLocked(null, intendingUid, callingPackage);}}@Overridepublic ClipData getPrimaryClip(String pkg, @UserIdInt int userId) {final int intendingUid = getIntendingUid(pkg, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,intendingUid, intendingUserId)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {try {addActiveOwnerLocked(intendingUid, pkg);} catch (SecurityException e) {// Permission could not be granted - URI may be invalidSlog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");setPrimaryClipInternalLocked(null, intendingUid, pkg);return null;}PerUserClipboard clipboard = getClipboardLocked(intendingUserId);showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);notifyTextClassifierLocked(clipboard, pkg, intendingUid);return clipboard.primaryClip;}}@Overridepublic ClipDescription getPrimaryClipDescription(String callingPackage,@UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);return clipboard.primaryClip != null? clipboard.primaryClip.getDescription() : null;}}@Overridepublic boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return false;}synchronized (mLock) {return getClipboardLocked(intendingUserId).primaryClip != null;}}@Overridepublic void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,String callingPackage, @UserIdInt int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);synchronized (mLock) {getClipboardLocked(intendingUserId).primaryClipListeners.register(listener,new ListenerInfo(intendingUid, callingPackage));}}@Overridepublic void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,String callingPackage, @UserIdInt int userId) {final int intendingUserId = getIntendingUserId(callingPackage, userId);synchronized (mLock) {getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);}}@Overridepublic boolean hasClipboardText(String callingPackage, int userId) {final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return false;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);if (clipboard.primaryClip != null) {CharSequence text = clipboard.primaryClip.getItemAt(0).getText();return text != null && text.length() > 0;}return false;}}@Overridepublic String getPrimaryClipSource(String callingPackage, int userId) {getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,"Requires SET_CLIP_SOURCE permission");final int intendingUid = getIntendingUid(callingPackage, userId);final int intendingUserId = UserHandle.getUserId(intendingUid);if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,intendingUid, intendingUserId, false)|| isDeviceLocked(intendingUserId)) {return null;}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(intendingUserId);if (clipboard.primaryClip != null) {return clipboard.mPrimaryClipPackage;}return null;}}};@GuardedBy("mLock")private PerUserClipboard getClipboardLocked(@UserIdInt int userId) {PerUserClipboard puc = mClipboards.get(userId);if (puc == null) {puc = new PerUserClipboard(userId);mClipboards.put(userId, puc);}return puc;}List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {final List<UserInfo> related;final long origId = Binder.clearCallingIdentity();try {related = mUm.getProfiles(userId, true);} catch (RemoteException e) {Slog.e(TAG, "Remote Exception calling UserManager: " + e);return null;} finally{Binder.restoreCallingIdentity(origId);}return related;}/** Check if the user has the given restriction set. Default to true if error occured during* calling UserManager, so it fails safe.*/private boolean hasRestriction(String restriction, int userId) {try {return mUm.hasUserRestriction(restriction, userId);} catch (RemoteException e) {Slog.e(TAG, "Remote Exception calling UserManager.getUserRestrictions: ", e);// Fails safereturn true;}}void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {synchronized (mLock) {setPrimaryClipInternalLocked(clip, uid, null);}}@GuardedBy("mLock")private void setPrimaryClipInternalLocked(@Nullable ClipData clip, int uid, @Nullable String sourcePackage) {mEmulatorClipboardMonitor.accept(clip);final int userId = UserHandle.getUserId(uid);if (clip != null) {startClassificationLocked(clip, userId);}// Update this usersetPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);// Update related usersList<UserInfo> related = getRelatedProfiles(userId);if (related != null) {int size = related.size();if (size > 1) { // Related profiles list include the current profile.final boolean canCopy = !hasRestriction(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, userId);// Copy clip data to related users if allowed. If disallowed, then remove// primary clip in related users to prevent pasting stale content.if (!canCopy) {clip = null;} else if (clip == null) {// do nothing for canCopy == true and clip == null case// To prevent from NPE happen in 'new ClipData(clip)' when run// android.content.cts.ClipboardManagerTest#testClearPrimaryClip} else {// We want to fix the uris of the related user's clip without changing the// uris of the current user's clip.// So, copy the ClipData, and then copy all the items, so that nothing// is shared in memory.clip = new ClipData(clip);for (int i = clip.getItemCount() - 1; i >= 0; i--) {clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));}clip.fixUrisLight(userId);}for (int i = 0; i < size; i++) {int id = related.get(i).id;if (id != userId) {final boolean canCopyIntoProfile = !hasRestriction(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);if (canCopyIntoProfile) {setPrimaryClipInternalLocked(getClipboardLocked(id), clip, uid, sourcePackage);}}}}}}void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,int uid) {synchronized ("mLock") {setPrimaryClipInternalLocked(clipboard, clip, uid, null);}}@GuardedBy("mLock")private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,int uid, @Nullable String sourcePackage) {revokeUris(clipboard);clipboard.activePermissionOwners.clear();if (clip == null && clipboard.primaryClip == null) {return;}clipboard.primaryClip = clip;clipboard.mNotifiedUids.clear();clipboard.mNotifiedTextClassifierUids.clear();if (clip != null) {clipboard.primaryClipUid = uid;clipboard.mPrimaryClipPackage = sourcePackage;} else {clipboard.primaryClipUid = android.os.Process.NOBODY_UID;clipboard.mPrimaryClipPackage = null;}if (clip != null) {final ClipDescription description = clip.getDescription();if (description != null) {description.setTimestamp(System.currentTimeMillis());}}sendClipChangedBroadcast(clipboard);}private void sendClipChangedBroadcast(PerUserClipboard clipboard) {final long ident = Binder.clearCallingIdentity();final int n = clipboard.primaryClipListeners.beginBroadcast();try {for (int i = 0; i < n; i++) {try {ListenerInfo li = (ListenerInfo)clipboard.primaryClipListeners.getBroadcastCookie(i);if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName,li.mUid, UserHandle.getUserId(li.mUid))) {clipboard.primaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();}} catch (RemoteException e) {// The RemoteCallbackList will take care of removing// the dead object for us.}}} finally {clipboard.primaryClipListeners.finishBroadcast();Binder.restoreCallingIdentity(ident);}}@GuardedBy("mLock")private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) {CharSequence text = (clip.getItemCount() == 0) ? null : clip.getItemAt(0).getText();if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength) {clip.getDescription().setClassificationStatus(ClipDescription.CLASSIFICATION_NOT_PERFORMED);return;}TextClassifier classifier;final long ident = Binder.clearCallingIdentity();try {classifier = createTextClassificationManagerAsUser(userId).createTextClassificationSession(new TextClassificationContext.Builder(getContext().getPackageName(),TextClassifier.WIDGET_TYPE_CLIPBOARD).build());} finally {Binder.restoreCallingIdentity(ident);}if (text.length() > classifier.getMaxGenerateLinksTextLength()) {clip.getDescription().setClassificationStatus(ClipDescription.CLASSIFICATION_NOT_PERFORMED);return;}mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId));}@WorkerThreadprivate void doClassification(CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId) {TextLinks.Request request = new TextLinks.Request.Builder(text).build();TextLinks links = classifier.generateLinks(request);// Find the highest confidence for each entity in the text.ArrayMap<String, Float> confidences = new ArrayMap<>();for (TextLinks.TextLink link : links.getLinks()) {for (int i = 0; i < link.getEntityCount(); i++) {String entity = link.getEntity(i);float conf = link.getConfidenceScore(entity);if (conf > confidences.getOrDefault(entity, 0f)) {confidences.put(entity, conf);}}}synchronized (mLock) {PerUserClipboard clipboard = getClipboardLocked(userId);if (clipboard.primaryClip == clip) {applyClassificationAndSendBroadcastLocked(clipboard, confidences, links, classifier);// Also apply to related profiles if neededList<UserInfo> related = getRelatedProfiles(userId);if (related != null) {int size = related.size();for (int i = 0; i < size; i++) {int id = related.get(i).id;if (id != userId) {final boolean canCopyIntoProfile = !hasRestriction(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);if (canCopyIntoProfile) {PerUserClipboard relatedClipboard = getClipboardLocked(id);if (hasTextLocked(relatedClipboard, text)) {applyClassificationAndSendBroadcastLocked(relatedClipboard, confidences, links, classifier);}}}}}}}}@GuardedBy("mLock")private void applyClassificationAndSendBroadcastLocked(PerUserClipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,TextClassifier classifier) {clipboard.mTextClassifier = classifier;clipboard.primaryClip.getDescription().setConfidenceScores(confidences);if (!links.getLinks().isEmpty()) {clipboard.primaryClip.getItemAt(0).setTextLinks(links);}sendClipChangedBroadcast(clipboard);}@GuardedBy("mLock")private boolean hasTextLocked(PerUserClipboard clipboard, @NonNull CharSequence text) {return clipboard.primaryClip != null&& clipboard.primaryClip.getItemCount() > 0&& text.equals(clipboard.primaryClip.getItemAt(0).getText());}private boolean isDeviceLocked(@UserIdInt int userId) {final long token = Binder.clearCallingIdentity();try {final KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class);return keyguardManager != null && keyguardManager.isDeviceLocked(userId);} finally {Binder.restoreCallingIdentity(token);}}private void checkUriOwner(Uri uri, int sourceUid) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {// This will throw SecurityException if caller can't grantmUgmInternal.checkGrantUriPermission(sourceUid, null,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));} finally {Binder.restoreCallingIdentity(ident);}}private void checkItemOwner(ClipData.Item item, int uid) {if (item.getUri() != null) {checkUriOwner(item.getUri(), uid);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {checkUriOwner(intent.getData(), uid);}}private void checkDataOwner(ClipData data, int uid) {final int N = data.getItemCount();for (int i=0; i<N; i++) {checkItemOwner(data.getItemAt(i), uid);}}private void grantUriPermission(Uri uri, int sourceUid, String targetPkg,int targetUserId) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {mUgm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),targetUserId);} catch (RemoteException ignored) {// Ignored because we're in same process} finally {Binder.restoreCallingIdentity(ident);}}private void grantItemPermission(ClipData.Item item, int sourceUid, String targetPkg,int targetUserId) {if (item.getUri() != null) {grantUriPermission(item.getUri(), sourceUid, targetPkg, targetUserId);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {grantUriPermission(intent.getData(), sourceUid, targetPkg, targetUserId);}}@GuardedBy("mLock")private void addActiveOwnerLocked(int uid, String pkg) {final IPackageManager pm = AppGlobals.getPackageManager();final int targetUserHandle = UserHandle.getCallingUserId();final long oldIdentity = Binder.clearCallingIdentity();try {PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle);if (pi == null) {throw new IllegalArgumentException("Unknown package " + pkg);}if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {throw new SecurityException("Calling uid " + uid+ " does not own package " + pkg);}} catch (RemoteException e) {// Can't happen; the package manager is in the same process} finally {Binder.restoreCallingIdentity(oldIdentity);}PerUserClipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid));if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {final int N = clipboard.primaryClip.getItemCount();for (int i=0; i<N; i++) {grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid,pkg, UserHandle.getUserId(uid));}clipboard.activePermissionOwners.add(pkg);}}private void revokeUriPermission(Uri uri, int sourceUid) {if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;final long ident = Binder.clearCallingIdentity();try {mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner,ContentProvider.getUriWithoutUserId(uri),Intent.FLAG_GRANT_READ_URI_PERMISSION,ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));} finally {Binder.restoreCallingIdentity(ident);}}private void revokeItemPermission(ClipData.Item item, int sourceUid) {if (item.getUri() != null) {revokeUriPermission(item.getUri(), sourceUid);}Intent intent = item.getIntent();if (intent != null && intent.getData() != null) {revokeUriPermission(intent.getData(), sourceUid);}}private void revokeUris(PerUserClipboard clipboard) {if (clipboard.primaryClip == null) {return;}final int N = clipboard.primaryClip.getItemCount();for (int i=0; i<N; i++) {revokeItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid);}}private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,@UserIdInt int userId) {return clipboardAccessAllowed(op, callingPackage, uid, userId, true);}private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,@UserIdInt int userId, boolean shouldNoteOp) {boolean allowed;// First, verify package ownership to ensure use below is safe.mAppOps.checkPackage(uid, callingPackage);// Shell can access the clipboard for testing purposes.if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,callingPackage) == PackageManager.PERMISSION_GRANTED) {allowed = true;} else {// The default IME is always allowed to access the clipboard.allowed = isDefaultIme(userId, callingPackage);}switch (op) {case AppOpsManager.OP_READ_CLIPBOARD:// Clipboard can only be read by applications with focus..// or the application have the INTERNAL_SYSTEM_WINDOW and INTERACT_ACROSS_USERS_FULL// at the same time. e.x. SystemUI. It needs to check the window focus of// Binder.getCallingUid(). Without checking, the user X can't copy any thing from// INTERNAL_SYSTEM_WINDOW to the other applications.if (!allowed) {allowed = mWm.isUidFocused(uid)|| isInternalSysWindowAppWithWindowFocus(callingPackage);}if (!allowed && mContentCaptureInternal != null) {// ...or the Content Capture Service// The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser// is used to check if the uid has the permission BIND_CONTENT_CAPTURE_SERVICE.// if the application has the permission, let it to access user's clipboard.// To passed synthesized uid user#10_app#systemui may not tell the real uid.// userId must pass intending userId. i.e. user#10.allowed = mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId);}if (!allowed && mAutofillInternal != null) {// ...or the Augmented Autofill Service// The uid parameter of mAutofillInternal.isAugmentedAutofillServiceForUser// is used to check if the uid has the permission BIND_AUTOFILL_SERVICE.// if the application has the permission, let it to access user's clipboard.// To passed synthesized uid user#10_app#systemui may not tell the real uid.// userId must pass intending userId. i.e. user#10.allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);}break;case AppOpsManager.OP_WRITE_CLIPBOARD:// Writing is allowed without focus.allowed = true;break;default:throw new IllegalArgumentException("Unknown clipboard appop " + op);}if (!allowed) {Slog.e(TAG, "Denying clipboard access to " + callingPackage+ ", application is not in focus nor is it a system service for "+ "user " + userId);return false;}// Finally, check the app op.int appOpsResult;if (shouldNoteOp) {appOpsResult = mAppOps.noteOp(op, uid, callingPackage);} else {appOpsResult = mAppOps.checkOp(op, uid, callingPackage);}return appOpsResult == AppOpsManager.MODE_ALLOWED;}private boolean isDefaultIme(int userId, String packageName) {String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),Settings.Secure.DEFAULT_INPUT_METHOD, userId);if (!TextUtils.isEmpty(defaultIme)) {final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();return imePkg.equals(packageName);}return false;}/*** Shows a toast to inform the user that an app has accessed the clipboard. This is only done if* the setting is enabled, and if the accessing app is not the source of the data and is not the* IME, the content capture service, or the autofill service. The notification is also only* shown once per clip for each app.*/@GuardedBy("mLock")private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,PerUserClipboard clipboard) {if (clipboard.primaryClip == null) {return;}if (Settings.Secure.getInt(getContext().getContentResolver(),Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,(mShowAccessNotifications ? 1 : 0)) == 0) {return;}// Don't notify if the app accessing the clipboard is the same as the current owner.if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {return;}// Exclude special cases: IME, ContentCapture, Autofill.if (isDefaultIme(userId, callingPackage)) {return;}if (mContentCaptureInternal != null&& mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {return;}if (mAutofillInternal != null&& mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {return;}// Don't notify if already notified for this uid and clip.if (clipboard.mNotifiedUids.get(uid)) {return;}clipboard.mNotifiedUids.put(uid, true);Binder.withCleanCallingIdentity(() -> {try {CharSequence callingAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(callingPackage, 0, userId));String message =getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);Slog.i(TAG, message);/* Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT).show(); */} catch (PackageManager.NameNotFoundException e) {// do nothing}});}/*** Returns true if the provided {@link ClipData} represents a single piece of text. That is, if* there is only on {@link ClipData.Item}, and that item contains a non-empty piece of text and* no URI or Intent. Note that HTML may be provided along with text so the presence of* HtmlText in the clip does not prevent this method returning true.*/private static boolean isText(@NonNull ClipData data) {if (data.getItemCount() > 1) {return false;}ClipData.Item item = data.getItemAt(0);return !TextUtils.isEmpty(item.getText()) && item.getUri() == null&& item.getIntent() == null;}/** Potentially notifies the text classifier that an app is accessing a text clip. */@GuardedBy("mLock")private void notifyTextClassifierLocked(PerUserClipboard clipboard, String callingPackage, int callingUid) {if (clipboard.primaryClip == null) {return;}ClipData.Item item = clipboard.primaryClip.getItemAt(0);if (item == null) {return;}if (!isText(clipboard.primaryClip)) {return;}TextClassifier textClassifier = clipboard.mTextClassifier;// Don't notify text classifier if we haven't used it to annotate the text in the clip.if (textClassifier == null) {return;}// Don't notify text classifier if the app reading the clipboard does not have the focus.if (!mWm.isUidFocused(callingUid)) {return;}// Don't notify text classifier again if already notified for this uid and clip.if (clipboard.mNotifiedTextClassifierUids.get(callingUid)) {return;}clipboard.mNotifiedTextClassifierUids.put(callingUid, true);Binder.withCleanCallingIdentity(() -> {TextClassifierEvent.TextLinkifyEvent pasteEvent =new TextClassifierEvent.TextLinkifyEvent.Builder(TextClassifierEvent.TYPE_READ_CLIPBOARD).setEventContext(new TextClassificationContext.Builder(callingPackage, TextClassifier.WIDGET_TYPE_CLIPBOARD).build()).setExtras(Bundle.forPair("source_package", clipboard.mPrimaryClipPackage)).build();textClassifier.onTextClassifierEvent(pasteEvent);});}private TextClassificationManager createTextClassificationManagerAsUser(@UserIdInt int userId) {Context context = getContext().createContextAsUser(UserHandle.of(userId), /* flags= */ 0);return context.getSystemService(TextClassificationManager.class);}
}

6.解决后效果如下:

再次到浏览器使用剪切板复制文本内容,发现没有出现toast,故此bug已解决。

在这里插入图片描述

7.总结:

今天遇到的问题在Android12 Rom定制时使用剪贴板复制成功后会出现一个toast提示,需要根据日志排查定位到问题原因,解决也很简单,删除或者注释toast即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/906653.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/906653.shtml
英文地址,请注明出处:http://en.pswp.cn/news/906653.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SOC-ESP32S3部分:9-GPIO输入按键状态读取

飞书文档https://x509p6c8to.feishu.cn/wiki/L6IGwHKV6ikQ08kqwAwcAvhznBc 前面我们学习了GPIO的输出&#xff0c;GPIO输入部分其实也是一样的&#xff0c;这里我们使用按键作为GPIO输入例程讲解&#xff0c;分三步走。 查看板卡原理图&#xff0c;确定使用的是哪个GPIO查看G…

高可用集群keepalived

1.不同操作系统的安装 1.1 不同系统编译安装 ubuntu环境 apt-get - y install libssl-dev libpopt-dev daemon build-essential libssl-dev openssl libpopt-dev libsnmp-dev libnl-3-dev libnl-genl-3-dev centos环境 &#xff08;其他的下同&#xff09; yum install - y…

SpringCloud - 整合MQ实现消息总线服务

一、背景介绍 每当修改配置文件内容&#xff0c;如果需要客户端也同步更新&#xff0c;就需要手动调用/refresh接口&#xff0c;以便客户端能获取到最新的配置内容。 当客户端越来越多的时候&#xff0c;通过人工进行处理显然非常鸡肋。有没有一种更加高效的办法&#xff0c;…

测试W5500的第3步_使用ioLibrary库创建TCPServer

W5500是一款具有8个Socket的网络芯片&#xff0c;支持TCP Server模式&#xff0c;最多可同时连接8个客户端。本文介绍了基于STM32F10x和W5500的TCP Server实现&#xff0c;包括SPI初始化、W5500复位、网络参数配置、Socket状态管理等功能&#xff0c;适用于需要多客户端连接的嵌…

Web攻防-SQL注入数据库类型用户权限架构分层符号干扰利用过程发现思路

知识点&#xff1a; 1、Web攻防-SQL注入-产生原理&应用因素 2、Web攻防-SQL注入-各类数据库类型利用 演示案例-WEB攻防-SQL注入-数据库类型&架构分层&符号干扰 一、数据库知识 1、数据库名&#xff0c;表名&#xff0c;列名&#xff0c;数据 2、自带数据库&…

手机合集(不定期更新)

一、华为手机&#xff1a; 1.华为手机自助维修的方法&#xff1a; https://blog.csdn.net/humors221/article/details/145946128 2.华为手机实用功能介绍&#xff1a; https://blog.csdn.net/humors221/article/details/132514011 3.华为手机清理大数据的方法&#xff1a;…

移动安全Android——ROOT检测绕过

工具准备 Magisk GitHub - topjohnwu/Magisk: The Magic Mask for Android ZygisckNext GitHub - Dr-TSNG/ZygiskNext at v1.2.8 Shamiko Releases LSPosed/LSPosed.github.io 安卓ROOT教程 Magisk 安装教程 - Magisk 中文网 问题 大多数手机在ROOT状态下会出现APP闪…

Python高效网络爬虫开发指南

Python 网络爬虫入门与实战 一、引言 随着互联网数据的爆炸性增长&#xff0c;获取和分析这些数据变得越来越重要。网络爬虫作为数据采集的重要工具&#xff0c;在这其中扮演了不可或缺的角色。 二、环境搭建 首先我们需要安装Python环境以及一些必要的库&#xff1a; req…

wireshark: Display Filter Reference

https://www.wireshark.org/docs/dfref/// 这个里面的扩展功能还是很强大&#xff0c;可以帮着问题分析。支持大量的自定义化的字段读取功能&#xff0c;支持很多的协议。 https://www.wireshark.org/docs/dfref///f/frame.html frame.time_delta Time delta from previous ca…

dify创建银行客服系统例子

传统的银行客服系统&#xff0c;通常以会话管理的方式实现&#xff0c;配置繁琐复杂&#xff0c;固定且不灵活。如&#xff1a; 智能体的出现&#xff0c;为实现银行客服系统提供了想象空间&#xff0c;可以集知识库和业务流程为一体实现灵活可控的智能客服系统&#xff0c;即能…

前端函数防抖(Debounce)完整讲解 - 从原理、应用到完整实现

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

服务接口鉴权与内部认证:自定义注解与AOP实现的企业级实践

本文深入解析企业级系统中接口安全管控的核心需求&#xff0c;提出基于Spring AOP与自定义注解的轻量级鉴权方案。通过解构注解元数据定义、切面拦截逻辑、上下文传递机制等关键技术环节&#xff0c;系统阐述零侵入式鉴权体系的构建路径。结合金融支付网关、多租户SaaS平台、物…

26考研|高等代数:线性变换

前言 线性变换这一章节是考频较高的一部分&#xff0c;此部分涉及考点较多&#xff0c;涉及的考题也较多&#xff0c;学习线性变换时&#xff0c;应该注意搭建线性变换与矩阵之间的联系&#xff0c;掌握如何利用矩阵表示一个线性变换结构&#xff0c;同时介绍了最简单的线性变…

电磁兼容(EMC)仿真(精编版)

写在前面 本系列文章主要讲解电磁兼容(EMC)仿真的相关知识,希望能帮助更多的同学认识和了解电磁兼容(EMC)仿真。 若有相关问题,欢迎评论沟通,共同进步。(*^▽^*) 随着产品复杂性和密集度的提高以及设计周期的不断缩短,在设计周期的后期解决电磁兼容性(EMC)问题变得…

解决:dpkg: error: dpkg frontend lock is locked by another process

1、等待其他进程完成 如果后台有其他包管理操作&#xff08;如自动更新、软件安装等&#xff09;&#xff0c;等待几分钟再重试。 可以通过以下命令查看是否有相关进程&#xff1a; ps aux | grep -E apt|apt-get|dpkg 2、强制终止占用锁的进程 如果确认没有其他包管理操作&…

LVGL(lv_textarea文本框控件)

文章目录 一、lv_textarea 是什么&#xff1f;二、基本用法1. 创建 lv_textarea 对象2. 设置提示文字&#xff08;占位符&#xff09;3. 设置最大长度4. 设置密码模式&#xff08;显示为\*号&#xff09;5. 获取和设置内容6. 配合虚拟键盘使用&#xff08;常用于触摸屏&#xf…

【Java高阶面经:数据库篇】18、分布式事务:如何在分库分表中实现高性能与一致性?

一、分布式事务核心挑战:分库分表下的一致性困境 在分布式系统架构中,分库分表通过将数据分散存储提升了扩展性和性能,但却打破了传统单库事务的边界,使得分布式事务成为保障数据一致性的核心难题。其挑战主要体现在以下三方面: 1.1 ACID特性的分布式撕裂 原子性(Atomi…

【云呐】房地产企业固定资产如何管理

房产类固定资产如办公楼、门面房、宿舍楼、库房等&#xff0c;价值高、使用年限长、权属复杂、变更流程多&#xff0c;是企业最为关键的资产类型之一。管理房产类资产不仅要“看得到”&#xff0c;更要“管得住”。 房产资产管理应从权属明确开始。固定资产管理系统支持房产资产…

oracle数据库生成awr报告,排查数据库服务器CPU100%,系统卡顿,慢sql,根据sqlid查询关键信息,如会话SID,客户端机器名

AWR报告简介 AWR是Oracle 10g版本推出的特性,全称叫做 Automatic Workload Repository 全自动负载信息库 。Oracle启动后,会有后台进程定时采集并保存系统快照信息,也可以手工创建快照。AWR通过对比两个时间点的快照信息,生成该时间段的AWR报告,帮助DBA或开发人员了解 Ora…

kafka吞吐量提升总结

前言 原本自以为阅读了很久kafka的源码&#xff0c;对于kafka的了解已经深入到一定程度了&#xff0c;后面在某大厂的面试中&#xff0c;面试官询问我&#xff0c;如果需要提升kafka的性能&#xff0c;应该怎么做&#xff0c;我发现我能答上来的点非常的少&#xff0c;也暴露了…