Merge "Notify TextClassifier an app is reading the clipboard" into sc-dev

This commit is contained in:
Tony Mak
2021-04-14 13:57:43 +00:00
committed by Android (Google) Code Review
2 changed files with 73 additions and 16 deletions

View File

@@ -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+.
}
@@ -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;

View File

@@ -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;
@@ -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;
@@ -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;
}
@@ -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;
}
}
@@ -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;
@@ -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 {
@@ -627,17 +645,11 @@ public class ClipboardService extends SystemService {
new TextClassificationContext.Builder(
getContext().getPackageName(),
TextClassifier.WIDGET_TYPE_CLIPBOARD
).build()
);
).build()
);
} 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()) {
@@ -645,7 +657,7 @@ public class ClipboardService extends SystemService {
ClipDescription.CLASSIFICATION_NOT_PERFORMED);
return;
}
getClipboardLocked(userId).mTextClassifier = classifier;
mWorkerHandler.post(() -> doClassification(text, clip, classifier));
}
@@ -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<>();
@@ -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);