From 90a76b1ee8bc7208ebe2455da6469a6de77a333e Mon Sep 17 00:00:00 2001 From: octocorvus Date: Sat, 11 May 2024 08:04:38 +0000 Subject: [PATCH] support for allowing one-time clipboard access to apps --- core/res/res/values/strings.xml | 1 + core/res/res/values/symbols.xml | 1 + .../clipboard/ClipboardManagerInternal.java | 8 ++++++ .../server/clipboard/ClipboardHelper.java | 26 +++++++++++++++++- .../server/clipboard/ClipboardService.java | 27 ++++++++++++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 services/core/java/android/content/clipboard/ClipboardManagerInternal.java diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 99b229f96c64..7dab3893b704 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -6393,6 +6393,7 @@ ul. Switch to %1$s Don’t show again + Allow this time Missing permission %1$s tried to access sensors diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 41c1ae01639b..24fb431a9a78 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5315,6 +5315,7 @@ + diff --git a/services/core/java/android/content/clipboard/ClipboardManagerInternal.java b/services/core/java/android/content/clipboard/ClipboardManagerInternal.java new file mode 100644 index 000000000000..801a6a26c7e3 --- /dev/null +++ b/services/core/java/android/content/clipboard/ClipboardManagerInternal.java @@ -0,0 +1,8 @@ +package android.content.clipboard; + +/** + * @hide + */ +public abstract class ClipboardManagerInternal { + public abstract void setAllowOneTimeAccess(String pkg, int userId); +} diff --git a/services/core/java/com/android/server/clipboard/ClipboardHelper.java b/services/core/java/com/android/server/clipboard/ClipboardHelper.java index 7020ece5731d..70a3d4c1fc7b 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardHelper.java +++ b/services/core/java/com/android/server/clipboard/ClipboardHelper.java @@ -1,5 +1,8 @@ package com.android.server.clipboard; +import static android.content.Context.DEVICE_ID_DEFAULT; + +import android.content.clipboard.ClipboardManagerInternal; import android.content.ClipData; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -8,15 +11,22 @@ import android.ext.SettingsIntents; import android.ext.settings.app.AswDenyClipboardRead; import android.os.Process; +import android.os.UserHandle; import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.ext.AppSwitchNotification; +import com.android.server.ext.IntentReceiver; import com.android.server.pm.pkg.GosPackageStatePm; class ClipboardHelper { static final ClipData dummyClip = ClipData.newPlainText(null, ""); + static int getPackageUid(String pkgName, int userId) { + var pmi = LocalServices.getService(PackageManagerInternal.class); + return pmi.getPackageUid(pkgName, 0, userId); + } + static boolean isReadBlockedForPackage(Context ctx, String pkgName, int userId) { var pmi = LocalServices.getService(PackageManagerInternal.class); ApplicationInfo appInfo = pmi.getApplicationInfo(pkgName, 0, Process.SYSTEM_UID, userId); @@ -27,7 +37,7 @@ static boolean isReadBlockedForPackage(Context ctx, String pkgName, int userId) return AswDenyClipboardRead.I.get(ctx, userId, appInfo, ps); } - static void maybeNotifyAccessDenied(Context ctx, String pkgName, int userId) { + static void maybeNotifyAccessDenied(Context ctx, String pkgName, int userId, int deviceId) { var pmi = LocalServices.getService(PackageManagerInternal.class); ApplicationInfo appInfo = pmi.getApplicationInfo(pkgName, 0, Process.SYSTEM_UID, userId); if (appInfo == null) { @@ -36,6 +46,20 @@ static void maybeNotifyAccessDenied(Context ctx, String pkgName, int userId) { var n = AppSwitchNotification.create(ctx, appInfo, SettingsIntents.APP_CLIPBOARD); n.titleRes = R.string.notif_clipboard_read_deny_title; n.gosPsFlagSuppressNotif = GosPackageState.FLAG_DENY_CLIPBOARD_READ_SUPPRESS_NOTIF; + // only show notification action to allow one-time access for default device + if (deviceId == DEVICE_ID_DEFAULT) { + IntentReceiver receiver = IntentReceiver.getInstance( + NotifActionReceiver.class, NotifActionReceiver::new, ctx); + n.setCustomNotifAction(receiver, R.string.notification_action_allow_this_time); + } n.maybeShow(); } + + static class NotifActionReceiver extends AppSwitchNotification.CustomNotifActionReceiver { + @Override + public void onReceive(Context ctx, String packageName, UserHandle user) { + var cmi = LocalServices.getService(ClipboardManagerInternal.class); + cmi.setAllowOneTimeAccess(packageName, user.getIdentifier()); + } + } } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index f6408aa85e8c..a5382921040f 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -48,6 +48,7 @@ import android.content.IOnPrimaryClipChangedListener; import android.content.Intent; import android.content.IntentFilter; +import android.content.clipboard.ClipboardManagerInternal; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -212,6 +213,8 @@ public ClipboardService(Context context) { HandlerThread workerThread = new HandlerThread(TAG); workerThread.start(); mWorkerHandler = workerThread.getThreadHandler(); + + LocalServices.addService(ClipboardManagerInternal.class, new ClipboardManagerInternalImpl()); } @Override @@ -322,6 +325,9 @@ private static class Clipboard { */ final SparseBooleanArray mNotifiedTextClassifierUids = new SparseBooleanArray(); + /** Uids that have access to current clip. */ + final SparseBooleanArray mCurrentClipAllowedUids = new SparseBooleanArray(); + final HashSet activePermissionOwners = new HashSet(); @@ -888,6 +894,23 @@ public void handleMessage(@NonNull Message msg) { } }; + private class ClipboardManagerInternalImpl extends ClipboardManagerInternal { + @Override + public void setAllowOneTimeAccess(String pkg, int userId) { + synchronized (mLock) { + int pkgUid = ClipboardHelper.getPackageUid(pkg, userId); + if (pkgUid < 0) { + return; + } + Clipboard clipboard = getClipboardLocked(userId, DEVICE_ID_DEFAULT); + if (clipboard == null) { + return; + } + clipboard.mCurrentClipAllowedUids.put(pkgUid, true); + } + } + } + @GuardedBy("mLock") private @Nullable Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) { Clipboard clipboard = mClipboards.get(userId, deviceId); @@ -1034,6 +1057,7 @@ private void setPrimaryClipInternalNoClassifyLocked(Clipboard clipboard, clipboard.primaryClip = clip; clipboard.mNotifiedUids.clear(); clipboard.mNotifiedTextClassifierUids.clear(); + clipboard.mCurrentClipAllowedUids.clear(); if (clip != null) { clipboard.primaryClipUid = uid; clipboard.mPrimaryClipPackage = sourcePackage; @@ -1498,7 +1522,7 @@ private void showAccessNotificationLocked(String callingPackage, int uid, @UserI Slog.d(TAG, "clipboard read blocked for: " + callingPackage); Binder.withCleanCallingIdentity( () -> ClipboardHelper.maybeNotifyAccessDenied( - getContext(), callingPackage, userId)); + getContext(), callingPackage, userId, clipboard.deviceId)); return; } @@ -1648,6 +1672,7 @@ private TextClassificationManager createTextClassificationManagerAsUser(@UserIdI private boolean isReadBlockedForPkg(int uid, String pkg, int userId, Clipboard clipboard) { return uid != clipboard.primaryClipUid + && !clipboard.mCurrentClipAllowedUids.get(uid) && ClipboardHelper.isReadBlockedForPackage(getContext(), pkg, userId); } }