Loading core/java/android/view/textclassifier/TextClassifierEvent.java +9 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,8 @@ public abstract class TextClassifierEvent implements Parcelable { TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED}) TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED, TYPE_READ_CLIPBOARD}) public @interface Type { // For custom event types, use range 1,000,000+. } Loading Loading @@ -135,6 +136,13 @@ public abstract class TextClassifierEvent implements Parcelable { public static final int TYPE_ACTIONS_GENERATED = 20; /** Some text links were generated.*/ public static final int TYPE_LINKS_GENERATED = 21; /** * Read a clipboard. * TODO: Make this public. * * @hide */ public static final int TYPE_READ_CLIPBOARD = 22; @Category private final int mEventCategory; Loading services/core/java/com/android/server/clipboard/ClipboardService.java +64 −15 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ 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; Loading @@ -67,6 +68,7 @@ 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; Loading Loading @@ -211,9 +213,18 @@ public class ClipboardService extends SystemService { /** 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; } Loading Loading @@ -371,6 +382,7 @@ public class ClipboardService extends SystemService { addActiveOwnerLocked(intendingUid, pkg); PerUserClipboard clipboard = getClipboardLocked(intendingUserId); showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard); notifyTextClassifierLocked(clipboard, pkg, intendingUid); return clipboard.primaryClip; } } Loading Loading @@ -580,6 +592,7 @@ public class ClipboardService extends SystemService { } clipboard.primaryClip = clip; clipboard.mNotifiedUids.clear(); clipboard.mNotifiedTextClassifierUids.clear(); if (clip != null) { clipboard.primaryClipUid = uid; clipboard.mPrimaryClipPackage = sourcePackage; Loading Loading @@ -619,6 +632,11 @@ public class ClipboardService extends SystemService { @GuardedBy("mLock") private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) { if (clip.getItemCount() == 0) { clip.getDescription().setClassificationStatus( ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } TextClassifier classifier; final long ident = Binder.clearCallingIdentity(); try { Loading @@ -632,12 +650,6 @@ public class ClipboardService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } if (clip.getItemCount() == 0) { clip.getDescription().setClassificationStatus( ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } CharSequence text = clip.getItemAt(0).getText(); if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength || text.length() > classifier.getMaxGenerateLinksTextLength()) { Loading @@ -645,7 +657,7 @@ public class ClipboardService extends SystemService { ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } getClipboardLocked(userId).mTextClassifier = classifier; mWorkerHandler.post(() -> doClassification(text, clip, classifier)); } Loading @@ -653,12 +665,7 @@ public class ClipboardService extends SystemService { private void doClassification( CharSequence text, ClipData clip, TextClassifier classifier) { TextLinks.Request request = new TextLinks.Request.Builder(text).build(); TextLinks links; try { links = classifier.generateLinks(request); } finally { classifier.destroy(); } TextLinks links = classifier.generateLinks(request); // Find the highest confidence for each entity in the text. ArrayMap<String, Float> confidences = new ArrayMap<>(); Loading Loading @@ -979,6 +986,48 @@ public class ClipboardService extends SystemService { && 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); Loading Loading
core/java/android/view/textclassifier/TextClassifierEvent.java +9 −1 Original line number Diff line number Diff line Loading @@ -86,7 +86,8 @@ public abstract class TextClassifierEvent implements Parcelable { TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED}) TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED, TYPE_READ_CLIPBOARD}) public @interface Type { // For custom event types, use range 1,000,000+. } Loading Loading @@ -135,6 +136,13 @@ public abstract class TextClassifierEvent implements Parcelable { public static final int TYPE_ACTIONS_GENERATED = 20; /** Some text links were generated.*/ public static final int TYPE_LINKS_GENERATED = 21; /** * Read a clipboard. * TODO: Make this public. * * @hide */ public static final int TYPE_READ_CLIPBOARD = 22; @Category private final int mEventCategory; Loading
services/core/java/com/android/server/clipboard/ClipboardService.java +64 −15 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ 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; Loading @@ -67,6 +68,7 @@ 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; Loading Loading @@ -211,9 +213,18 @@ public class ClipboardService extends SystemService { /** 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; } Loading Loading @@ -371,6 +382,7 @@ public class ClipboardService extends SystemService { addActiveOwnerLocked(intendingUid, pkg); PerUserClipboard clipboard = getClipboardLocked(intendingUserId); showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard); notifyTextClassifierLocked(clipboard, pkg, intendingUid); return clipboard.primaryClip; } } Loading Loading @@ -580,6 +592,7 @@ public class ClipboardService extends SystemService { } clipboard.primaryClip = clip; clipboard.mNotifiedUids.clear(); clipboard.mNotifiedTextClassifierUids.clear(); if (clip != null) { clipboard.primaryClipUid = uid; clipboard.mPrimaryClipPackage = sourcePackage; Loading Loading @@ -619,6 +632,11 @@ public class ClipboardService extends SystemService { @GuardedBy("mLock") private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) { if (clip.getItemCount() == 0) { clip.getDescription().setClassificationStatus( ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } TextClassifier classifier; final long ident = Binder.clearCallingIdentity(); try { Loading @@ -632,12 +650,6 @@ public class ClipboardService extends SystemService { } finally { Binder.restoreCallingIdentity(ident); } if (clip.getItemCount() == 0) { clip.getDescription().setClassificationStatus( ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } CharSequence text = clip.getItemAt(0).getText(); if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength || text.length() > classifier.getMaxGenerateLinksTextLength()) { Loading @@ -645,7 +657,7 @@ public class ClipboardService extends SystemService { ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } getClipboardLocked(userId).mTextClassifier = classifier; mWorkerHandler.post(() -> doClassification(text, clip, classifier)); } Loading @@ -653,12 +665,7 @@ public class ClipboardService extends SystemService { private void doClassification( CharSequence text, ClipData clip, TextClassifier classifier) { TextLinks.Request request = new TextLinks.Request.Builder(text).build(); TextLinks links; try { links = classifier.generateLinks(request); } finally { classifier.destroy(); } TextLinks links = classifier.generateLinks(request); // Find the highest confidence for each entity in the text. ArrayMap<String, Float> confidences = new ArrayMap<>(); Loading Loading @@ -979,6 +986,48 @@ public class ClipboardService extends SystemService { && 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); Loading