summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/ClipboardManager.java45
-rw-r--r--core/java/android/content/IClipboard.aidl26
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java3
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java17
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java399
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java27
7 files changed, 414 insertions, 114 deletions
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 85af87722ed2..06a3b1e1cc66 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -70,6 +70,23 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
public static final boolean DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+ /**
+ * DeviceConfig property, within the clipboard namespace, that determines whether VirtualDevices
+ * are allowed to have siloed Clipboards for the apps running on them. If false, then clipboard
+ * access is blocked entirely for apps running on VirtualDevices.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS =
+ "allow_virtualdevice_silos";
+
+ /**
+ * Default value for the DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS property.
+ *
+ * @hide
+ */
+ public static final boolean DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS = false;
+
private final Context mContext;
private final Handler mHandler;
private final IClipboard mService;
@@ -133,7 +150,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
clip,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -162,6 +180,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
mContext.getOpPackageName(),
mContext.getAttributionTag(),
mContext.getUserId(),
+ mContext.getDeviceId(),
sourcePackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -178,7 +197,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
mService.clearPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -197,7 +217,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
return mService.getPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -217,7 +238,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
return mService.getPrimaryClipDescription(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -234,7 +256,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
return mService.hasPrimaryClip(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -248,7 +271,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
mPrimaryClipChangedServiceListener,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -266,7 +290,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
mPrimaryClipChangedServiceListener,
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -306,7 +331,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
return mService.hasClipboardText(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -326,7 +352,8 @@ public class ClipboardManager extends android.text.ClipboardManager {
return mService.getPrimaryClipSource(
mContext.getOpPackageName(),
mContext.getAttributionTag(),
- mContext.getUserId());
+ mContext.getUserId(),
+ mContext.getDeviceId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index 46ece2bc3f5e..b2216d4953e6 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -26,22 +26,26 @@ import android.content.IOnPrimaryClipChangedListener;
* {@hide}
*/
interface IClipboard {
- void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId);
- void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag, int userId,
- String sourcePackage);
- void clearPrimaryClip(String callingPackage, String attributionTag, int userId);
- ClipData getPrimaryClip(String pkg, String attributionTag, int userId);
- ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag, int userId);
- boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId);
+ void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId,
+ int deviceId);
+ void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag,
+ int userId, int deviceId, String sourcePackage);
+ void clearPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId);
+ ClipData getPrimaryClip(String pkg, String attributionTag, int userId, int deviceId);
+ ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag,
+ int userId, int deviceId);
+ boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId, int deviceId);
void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, String attributionTag, int userId);
+ String callingPackage, String attributionTag, int userId, int deviceId);
void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, String attributionTag, int userId);
+ String callingPackage, String attributionTag, int userId, int deviceId);
/**
* Returns true if the clipboard contains text; false otherwise.
*/
- boolean hasClipboardText(String callingPackage, String attributionTag, int userId);
+ boolean hasClipboardText(String callingPackage, String attributionTag, int userId,
+ int deviceId);
- String getPrimaryClipSource(String callingPackage, String attributionTag, int userId);
+ String getPrimaryClipSource(String callingPackage, String attributionTag, int userId,
+ int deviceId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 5985ce45e4d5..3100277eb762 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -88,7 +88,6 @@ import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Consumer;
@@ -114,7 +113,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final CameraAccessController mCameraAccessController;
private VirtualAudioController mVirtualAudioController;
@VisibleForTesting
- final Set<Integer> mVirtualDisplayIds = new ArraySet<>();
+ final ArraySet<Integer> mVirtualDisplayIds = new ArraySet<>();
private final OnDeviceCloseListener mOnDeviceCloseListener;
private final IBinder mAppToken;
private final VirtualDeviceParams mParams;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 2395814cc4bf..f39c32df1844 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -67,7 +67,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -471,7 +470,7 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
- public @NonNull Set<Integer> getDeviceIdsForUid(int uid) {
+ public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
ArraySet<Integer> result = new ArraySet<>();
synchronized (mVirtualDeviceManagerLock) {
int size = mVirtualDevices.size();
@@ -587,6 +586,20 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId) {
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
+ if (device.getDeviceId() == deviceId) {
+ return new ArraySet<>(device.mVirtualDisplayIds);
+ }
+ }
+ }
+ return new ArraySet<>();
+ }
+
+ @Override
public void registerVirtualDisplayListener(
@NonNull VirtualDisplayListener listener) {
synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 6f9a17682dd7..ecff0a790559 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -17,6 +17,10 @@
package com.android.server.clipboard;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
+import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
+import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
+import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
import android.Manifest;
import android.annotation.NonNull;
@@ -29,6 +33,8 @@ import android.app.AppOpsManager;
import android.app.IUriGrantsManager;
import android.app.KeyguardManager;
import android.app.UriGrantsManager;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
@@ -39,11 +45,13 @@ import android.content.Context;
import android.content.IClipboard;
import android.content.IOnPrimaryClipChangedListener;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -64,10 +72,13 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
import android.util.SafetyProtectionUtils;
import android.util.Slog;
-import android.util.SparseArray;
+import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
+import android.view.Display;
import android.view.autofill.AutofillManagerInternal;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -130,7 +141,9 @@ public class ClipboardService extends SystemService {
private final IUriGrantsManager mUgm;
private final UriGrantsManagerInternal mUgmInternal;
private final WindowManagerInternal mWm;
- private final VirtualDeviceManagerInternal mVdm;
+ private final VirtualDeviceManagerInternal mVdmInternal;
+ private final VirtualDeviceManager mVdm;
+ private BroadcastReceiver mVirtualDeviceRemovedReceiver;
private final IUserManager mUm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
@@ -141,11 +154,15 @@ public class ClipboardService extends SystemService {
private final Handler mWorkerHandler;
@GuardedBy("mLock")
- private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
+ // Maps (userId, deviceId) to Clipboard.
+ private final SparseArrayMap<Integer, Clipboard> mClipboards = new SparseArrayMap<>();
@GuardedBy("mLock")
private boolean mShowAccessNotifications =
ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+ @GuardedBy("mLock")
+ private boolean mAllowVirtualDeviceSilos =
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS;
@GuardedBy("mLock")
private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
@@ -163,7 +180,9 @@ public class ClipboardService extends SystemService {
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mWm = LocalServices.getService(WindowManagerInternal.class);
// Can be null; not all products have CDM + VirtualDeviceManager
- mVdm = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+ mVdm = (mVdmInternal == null) ? null : getContext().getSystemService(
+ VirtualDeviceManager.class);
mPm = getContext().getPackageManager();
mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -174,7 +193,7 @@ public class ClipboardService extends SystemService {
if (IS_EMULATOR) {
mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
synchronized (mLock) {
- setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
+ setPrimaryClipInternalLocked(getClipboardLocked(0, DEVICE_ID_DEFAULT), clip,
android.os.Process.SYSTEM_UID, null);
}
});
@@ -194,12 +213,39 @@ public class ClipboardService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());
+ if (mVdmInternal != null) {
+ registerVirtualDeviceRemovedListener();
+ }
+ }
+
+ private void registerVirtualDeviceRemovedListener() {
+ if (mVirtualDeviceRemovedReceiver != null) {
+ return;
+ }
+ mVirtualDeviceRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) {
+ return;
+ }
+ final int removedDeviceId =
+ intent.getIntExtra(EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_INVALID);
+ synchronized (mLock) {
+ for (int i = mClipboards.numMaps() - 1; i >= 0; i--) {
+ mClipboards.delete(mClipboards.keyAt(i), removedDeviceId);
+ }
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter(ACTION_VIRTUAL_DEVICE_REMOVED);
+ getContext().registerReceiver(mVirtualDeviceRemovedReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
}
@Override
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
- mClipboards.remove(user.getUserIdentifier());
+ mClipboards.delete(user.getUserIdentifier());
}
}
@@ -209,6 +255,10 @@ public class ClipboardService extends SystemService {
DeviceConfig.NAMESPACE_CLIPBOARD,
ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ mAllowVirtualDeviceSilos = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CLIPBOARD,
+ ClipboardManager.DEVICE_CONFIG_ALLOW_VIRTUALDEVICE_SILOS,
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_ALLOW_VIRTUALDEVICE_SILOS);
mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
}
@@ -226,8 +276,9 @@ public class ClipboardService extends SystemService {
}
}
- private class PerUserClipboard {
- final int userId;
+ private static class Clipboard {
+ public final int userId;
+ public final int deviceId;
final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
= new RemoteCallbackList<IOnPrimaryClipChangedListener>();
@@ -254,8 +305,9 @@ public class ClipboardService extends SystemService {
/** The text classifier session that is used to annotate the text in the primary clip. */
TextClassifier mTextClassifier;
- PerUserClipboard(int userId) {
+ Clipboard(int userId, int deviceId) {
this.userId = userId;
+ this.deviceId = deviceId;
}
}
@@ -333,6 +385,54 @@ public class ClipboardService extends SystemService {
}
/**
+ * Determines which deviceId to use for selecting a Clipboard, depending on where a given app
+ * is running.
+ *
+ * @param requestedDeviceId the requested deviceId passed in from the client side
+ * @param uid the intended app uid
+ * @return a deviceId to use in selecting the appropriate clipboard, or
+ * DEVICE_ID_INVALID if this uid should not be allowed access. A value of DEVICE_ID_DEFAULT
+ * means just use the "regular" clipboard.
+ */
+ private int getIntendingDeviceId(int requestedDeviceId, int uid) {
+ if (mVdmInternal == null) {
+ return DEVICE_ID_DEFAULT;
+ }
+
+ ArraySet<Integer> virtualDeviceIds = mVdmInternal.getDeviceIdsForUid(uid);
+
+ synchronized (mLock) {
+ if (!mAllowVirtualDeviceSilos
+ && (!virtualDeviceIds.isEmpty() || requestedDeviceId != DEVICE_ID_DEFAULT)) {
+ return DEVICE_ID_INVALID;
+ }
+ }
+
+ if (requestedDeviceId != DEVICE_ID_DEFAULT) {
+ // Privileged apps that own the VirtualDevices, or regular apps running on it, can
+ // request it by id.
+ if (mVdmInternal.getDeviceOwnerUid(requestedDeviceId) == uid
+ || virtualDeviceIds.contains(requestedDeviceId)) {
+ return requestedDeviceId;
+ }
+ return DEVICE_ID_INVALID;
+ }
+
+ // The common case is apps running normally (not on a VirtualDevice).
+ if (virtualDeviceIds.isEmpty()) {
+ return DEVICE_ID_DEFAULT;
+ }
+
+ // If an app is running on more than one VirtualDevice, it isn't clear which clipboard they
+ // should use.
+ if (virtualDeviceIds.size() > 1) {
+ return DEVICE_ID_INVALID;
+ }
+
+ return virtualDeviceIds.valueAt(0);
+ }
+
+ /**
* To handle the difference between userId and intendingUserId, uid and intendingUid.
*
* userId means that comes from the calling side and should be validated by
@@ -364,8 +464,10 @@ public class ClipboardService extends SystemService {
ClipData clip,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
- checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, callingPackage);
+ @UserIdInt int userId,
+ int deviceId) {
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
+ callingPackage);
}
@Override
@@ -374,10 +476,12 @@ public class ClipboardService extends SystemService {
String callingPackage,
String attributionTag,
@UserIdInt int userId,
+ int deviceId,
String sourcePackage) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
- checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, sourcePackage);
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, deviceId,
+ sourcePackage);
}
private void checkAndSetPrimaryClip(
@@ -385,41 +489,46 @@ public class ClipboardService extends SystemService {
String callingPackage,
String attributionTag,
@UserIdInt int userId,
+ int deviceId,
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);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_WRITE_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
- intendingUserId)) {
+ intendingUserId,
+ intendingDeviceId)) {
return;
}
checkDataOwner(clip, intendingUid);
synchronized (mLock) {
- scheduleAutoClear(userId, intendingUid);
- setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
+ scheduleAutoClear(userId, intendingUid, intendingDeviceId);
+ setPrimaryClipInternalLocked(clip, intendingUid, intendingDeviceId, sourcePackage);
}
}
- private void scheduleAutoClear(@UserIdInt int userId, int intendingUid) {
+ private void scheduleAutoClear(
+ @UserIdInt int userId, int intendingUid, int intendingDeviceId) {
final long oldIdentity = Binder.clearCallingIdentity();
try {
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_AUTO_CLEAR_ENABLED, true)) {
+ Pair<Integer, Integer> userIdDeviceId = new Pair<>(userId, intendingDeviceId);
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
- userId);
+ userIdDeviceId);
Message clearMessage =
Message.obtain(
mClipboardClearHandler,
ClipboardClearHandler.MSG_CLEAR,
userId,
intendingUid,
- userId);
+ userIdDeviceId);
mClipboardClearHandler.sendMessageDelayed(clearMessage,
getTimeoutForAutoClear());
}
@@ -436,52 +545,57 @@ public class ClipboardService extends SystemService {
@Override
public void clearPrimaryClip(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_WRITE_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
- intendingUserId)) {
+ intendingUserId,
+ intendingDeviceId)) {
return;
}
synchronized (mLock) {
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
- userId);
- setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
+ new Pair<>(userId, deviceId));
+ setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, callingPackage);
}
}
@Override
- public ClipData getPrimaryClip(String pkg, String attributionTag, @UserIdInt int userId) {
+ public ClipData getPrimaryClip(
+ String pkg, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(pkg, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
pkg,
attributionTag,
intendingUid,
- intendingUserId)
+ intendingUserId,
+ intendingDeviceId)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
try {
- addActiveOwnerLocked(intendingUid, pkg);
+ addActiveOwnerLocked(intendingUid, intendingDeviceId, pkg);
} catch (SecurityException e) {
// Permission could not be granted - URI may be invalid
Slog.i(TAG, "Could not grant permission to primary clip. Clearing clipboard.");
- setPrimaryClipInternalLocked(null, intendingUid, pkg);
+ setPrimaryClipInternalLocked(null, intendingUid, intendingDeviceId, pkg);
return null;
}
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
if (clipboard.primaryClip != null) {
- scheduleAutoClear(userId, intendingUid);
+ scheduleAutoClear(userId, intendingUid, intendingDeviceId);
}
return clipboard.primaryClip;
}
@@ -489,21 +603,23 @@ public class ClipboardService extends SystemService {
@Override
public ClipDescription getPrimaryClipDescription(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
return clipboard.primaryClip != null
? clipboard.primaryClip.getDescription() : null;
}
@@ -511,21 +627,23 @@ public class ClipboardService extends SystemService {
@Override
public boolean hasPrimaryClip(
- String callingPackage, String attributionTag, @UserIdInt int userId) {
+ String callingPackage, String attributionTag, @UserIdInt int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
synchronized (mLock) {
- return getClipboardLocked(intendingUserId).primaryClip != null;
+ return getClipboardLocked(intendingUserId, intendingDeviceId).primaryClip != null;
}
}
@@ -534,11 +652,19 @@ public class ClipboardService extends SystemService {
IOnPrimaryClipChangedListener listener,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
+ @UserIdInt int userId,
+ int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
+ Slog.i(TAG, "addPrimaryClipChangedListener invalid deviceId for userId:"
+ + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage
+ + " requestedDeviceId:" + deviceId);
+ return;
+ }
synchronized (mLock) {
- getClipboardLocked(intendingUserId)
+ getClipboardLocked(intendingUserId, intendingDeviceId)
.primaryClipListeners
.register(
listener,
@@ -551,29 +677,41 @@ public class ClipboardService extends SystemService {
IOnPrimaryClipChangedListener listener,
String callingPackage,
String attributionTag,
- @UserIdInt int userId) {
+ @UserIdInt int userId,
+ int deviceId) {
+ final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = getIntendingUserId(callingPackage, userId);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
+ Slog.i(TAG, "removePrimaryClipChangedListener invalid deviceId for userId:"
+ + userId + " uid:" + intendingUid + " callingPackage:" + callingPackage);
+ return;
+ }
synchronized (mLock) {
- getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);
+ getClipboardLocked(intendingUserId,
+ intendingDeviceId).primaryClipListeners.unregister(listener);
}
}
@Override
- public boolean hasClipboardText(String callingPackage, String attributionTag, int userId) {
+ public boolean hasClipboardText(
+ String callingPackage, String attributionTag, int userId, int deviceId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
if (clipboard.primaryClip != null) {
CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
return text != null && text.length() > 0;
@@ -584,23 +722,25 @@ public class ClipboardService extends SystemService {
@Override
public String getPrimaryClipSource(
- String callingPackage, String attributionTag, int userId) {
+ String callingPackage, String attributionTag, int userId, int deviceId) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
+ final int intendingDeviceId = getIntendingDeviceId(deviceId, intendingUid);
if (!clipboardAccessAllowed(
AppOpsManager.OP_READ_CLIPBOARD,
callingPackage,
attributionTag,
intendingUid,
intendingUserId,
+ intendingDeviceId,
false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
+ Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId);
if (clipboard.primaryClip != null) {
return clipboard.mPrimaryClipPackage;
}
@@ -621,11 +761,13 @@ public class ClipboardService extends SystemService {
case MSG_CLEAR:
final int userId = msg.arg1;
final int intendingUid = msg.arg2;
+ final int intendingDeviceId = ((Pair<Integer, Integer>) msg.obj).second;
synchronized (mLock) {
- if (getClipboardLocked(userId).primaryClip != null) {
+ if (getClipboardLocked(userId, intendingDeviceId).primaryClip != null) {
FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
- setPrimaryClipInternalLocked(null, intendingUid, null);
+ setPrimaryClipInternalLocked(
+ null, intendingUid, intendingDeviceId, null);
}
}
break;
@@ -637,13 +779,13 @@ public class ClipboardService extends SystemService {
};
@GuardedBy("mLock")
- private PerUserClipboard getClipboardLocked(@UserIdInt int userId) {
- PerUserClipboard puc = mClipboards.get(userId);
- if (puc == null) {
- puc = new PerUserClipboard(userId);
- mClipboards.put(userId, puc);
+ private Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) {
+ Clipboard clipboard = mClipboards.get(userId, deviceId);
+ if (clipboard == null) {
+ clipboard = new Clipboard(userId, deviceId);
+ mClipboards.add(userId, deviceId, clipboard);
}
- return puc;
+ return clipboard;
}
List<UserInfo> getRelatedProfiles(@UserIdInt int userId) {
@@ -675,19 +817,20 @@ public class ClipboardService extends SystemService {
void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
synchronized (mLock) {
- setPrimaryClipInternalLocked(clip, uid, null);
+ setPrimaryClipInternalLocked(clip, uid, DEVICE_ID_DEFAULT, null);
}
}
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(
- @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
+ @Nullable ClipData clip, int uid, int deviceId, @Nullable String sourcePackage) {
mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
// Update this user
- setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
+ setPrimaryClipInternalLocked(getClipboardLocked(userId, deviceId), clip, uid,
+ sourcePackage);
// Update related users
List<UserInfo> related = getRelatedProfiles(userId);
@@ -722,7 +865,7 @@ public class ClipboardService extends SystemService {
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
setPrimaryClipInternalNoClassifyLocked(
- getClipboardLocked(id), clip, uid, sourcePackage);
+ getClipboardLocked(id, deviceId), clip, uid, sourcePackage);
}
}
}
@@ -730,7 +873,7 @@ public class ClipboardService extends SystemService {
}
}
- void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
+ void setPrimaryClipInternal(Clipboard clipboard, @Nullable ClipData clip,
int uid) {
synchronized (mLock) {
setPrimaryClipInternalLocked(clipboard, clip, uid, null);
@@ -738,18 +881,18 @@ public class ClipboardService extends SystemService {
}
@GuardedBy("mLock")
- private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
+ private void setPrimaryClipInternalLocked(Clipboard clipboard, @Nullable ClipData clip,
int uid, @Nullable String sourcePackage) {
final int userId = UserHandle.getUserId(uid);
if (clip != null) {
- startClassificationLocked(clip, userId);
+ startClassificationLocked(clip, userId, clipboard.deviceId);
}
setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
}
@GuardedBy("mLock")
- private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+ private void setPrimaryClipInternalNoClassifyLocked(Clipboard clipboard,
@Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
revokeUris(clipboard);
clipboard.activePermissionOwners.clear();
@@ -775,7 +918,7 @@ public class ClipboardService extends SystemService {
sendClipChangedBroadcast(clipboard);
}
- private void sendClipChangedBroadcast(PerUserClipboard clipboard) {
+ private void sendClipChangedBroadcast(Clipboard clipboard) {
final long ident = Binder.clearCallingIdentity();
final int n = clipboard.primaryClipListeners.beginBroadcast();
try {
@@ -789,7 +932,8 @@ public class ClipboardService extends SystemService {
li.mPackageName,
li.mAttributionTag,
li.mUid,
- UserHandle.getUserId(li.mUid))) {
+ UserHandle.getUserId(li.mUid),
+ clipboard.deviceId)) {
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
@@ -805,7 +949,8 @@ public class ClipboardService extends SystemService {
}
@GuardedBy("mLock")
- private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) {
+ private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId,
+ int deviceId) {
CharSequence text = (clip.getItemCount() == 0) ? null : clip.getItemAt(0).getText();
if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength) {
clip.getDescription().setClassificationStatus(
@@ -830,12 +975,13 @@ public class ClipboardService extends SystemService {
ClipDescription.CLASSIFICATION_NOT_PERFORMED);
return;
}
- mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId));
+ mWorkerHandler.post(() -> doClassification(text, clip, classifier, userId, deviceId));
}
@WorkerThread
private void doClassification(
- CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId) {
+ CharSequence text, ClipData clip, TextClassifier classifier, @UserIdInt int userId,
+ int deviceId) {
TextLinks.Request request = new TextLinks.Request.Builder(text).build();
TextLinks links = classifier.generateLinks(request);
@@ -852,7 +998,7 @@ public class ClipboardService extends SystemService {
}
synchronized (mLock) {
- PerUserClipboard clipboard = getClipboardLocked(userId);
+ Clipboard clipboard = getClipboardLocked(userId, deviceId);
if (clipboard.primaryClip == clip) {
applyClassificationAndSendBroadcastLocked(
clipboard, confidences, links, classifier);
@@ -867,7 +1013,7 @@ public class ClipboardService extends SystemService {
final boolean canCopyIntoProfile = !hasRestriction(
UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
if (canCopyIntoProfile) {
- PerUserClipboard relatedClipboard = getClipboardLocked(id);
+ Clipboard relatedClipboard = getClipboardLocked(id, deviceId);
if (hasTextLocked(relatedClipboard, text)) {
applyClassificationAndSendBroadcastLocked(
relatedClipboard, confidences, links, classifier);
@@ -882,7 +1028,7 @@ public class ClipboardService extends SystemService {
@GuardedBy("mLock")
private void applyClassificationAndSendBroadcastLocked(
- PerUserClipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,
+ Clipboard clipboard, ArrayMap<String, Float> confidences, TextLinks links,
TextClassifier classifier) {
clipboard.mTextClassifier = classifier;
clipboard.primaryClip.getDescription().setConfidenceScores(confidences);
@@ -893,7 +1039,7 @@ public class ClipboardService extends SystemService {
}
@GuardedBy("mLock")
- private boolean hasTextLocked(PerUserClipboard clipboard, @NonNull CharSequence text) {
+ private boolean hasTextLocked(Clipboard clipboard, @NonNull CharSequence text) {
return clipboard.primaryClip != null
&& clipboard.primaryClip.getItemCount() > 0
&& text.equals(clipboard.primaryClip.getItemAt(0).getText());
@@ -972,7 +1118,7 @@ public class ClipboardService extends SystemService {
}
@GuardedBy("mLock")
- private void addActiveOwnerLocked(int uid, String pkg) {
+ private void addActiveOwnerLocked(int uid, int deviceId, String pkg) {
final IPackageManager pm = AppGlobals.getPackageManager();
final int targetUserHandle = UserHandle.getCallingUserId();
final long oldIdentity = Binder.clearCallingIdentity();
@@ -990,7 +1136,7 @@ public class ClipboardService extends SystemService {
} finally {
Binder.restoreCallingIdentity(oldIdentity);
}
- PerUserClipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid));
+ Clipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid), deviceId);
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
final int N = clipboard.primaryClip.getItemCount();
for (int i=0; i<N; i++) {
@@ -1025,7 +1171,7 @@ public class ClipboardService extends SystemService {
}
}
- private void revokeUris(PerUserClipboard clipboard) {
+ private void revokeUris(Clipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
}
@@ -1036,8 +1182,14 @@ public class ClipboardService extends SystemService {
}
private boolean clipboardAccessAllowed(
- int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId) {
- return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId, true);
+ int op,
+ String callingPackage,
+ String attributionTag,
+ int uid,
+ @UserIdInt int userId,
+ int intendingDeviceId) {
+ return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId,
+ intendingDeviceId, true);
}
private boolean clipboardAccessAllowed(
@@ -1046,6 +1198,7 @@ public class ClipboardService extends SystemService {
String attributionTag,
int uid,
@UserIdInt int userId,
+ int intendingDeviceId,
boolean shouldNoteOp) {
boolean allowed;
@@ -1053,10 +1206,9 @@ public class ClipboardService extends SystemService {
// First, verify package ownership to ensure use below is safe.
mAppOps.checkPackage(uid, callingPackage);
- // Nothing in a virtual session is permitted to touch clipboard contents
- if (mVdm != null && mVdm.isAppRunningOnAnyVirtualDevice(uid)) {
+ if (intendingDeviceId == DEVICE_ID_INVALID) {
Slog.w(TAG, "Clipboard access denied to " + uid + "/" + callingPackage
- + " within a virtual device session");
+ + " due to invalid device id");
return false;
}
@@ -1077,7 +1229,8 @@ public class ClipboardService extends SystemService {
// 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)
+ allowed = isDefaultDeviceAndUidFocused(intendingDeviceId, uid)
+ || isVirtualDeviceAndUidFocused(intendingDeviceId, uid)
|| isInternalSysWindowAppWithWindowFocus(callingPackage);
}
if (!allowed && mContentCaptureInternal != null) {
@@ -1098,6 +1251,12 @@ public class ClipboardService extends SystemService {
// userId must pass intending userId. i.e. user#10.
allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
}
+ if (!allowed && intendingDeviceId != DEVICE_ID_DEFAULT) {
+ // Privileged apps which own a VirtualDevice are allowed to read its clipboard
+ // in the background.
+ allowed = (mVdmInternal != null)
+ && mVdmInternal.getDeviceOwnerUid(intendingDeviceId) == uid;
+ }
break;
case AppOpsManager.OP_WRITE_CLIPBOARD:
// Writing is allowed without focus.
@@ -1123,6 +1282,19 @@ public class ClipboardService extends SystemService {
return appOpsResult == AppOpsManager.MODE_ALLOWED;
}
+ private boolean isDefaultDeviceAndUidFocused(int intendingDeviceId, int uid) {
+ return intendingDeviceId == DEVICE_ID_DEFAULT && mWm.isUidFocused(uid);
+ }
+
+ private boolean isVirtualDeviceAndUidFocused(int intendingDeviceId, int uid) {
+ if (intendingDeviceId == DEVICE_ID_DEFAULT || mVdm == null) {
+ return false;
+ }
+ int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+ int focusedDeviceId = mVdm.getDeviceIdForDisplayId(topFocusedDisplayId);
+ return (focusedDeviceId == intendingDeviceId) && mWm.isUidFocused(uid);
+ }
+
private boolean isDefaultIme(int userId, String packageName) {
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, userId);
@@ -1141,7 +1313,7 @@ public class ClipboardService extends SystemService {
*/
@GuardedBy("mLock")
private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
- PerUserClipboard clipboard) {
+ Clipboard clipboard) {
if (clipboard.primaryClip == null) {
return;
}
@@ -1174,8 +1346,8 @@ public class ClipboardService extends SystemService {
if (clipboard.mNotifiedUids.get(uid)) {
return;
}
- clipboard.mNotifiedUids.put(uid, true);
+ final ArraySet<Context> toastContexts = getToastContexts(clipboard);
Binder.withCleanCallingIdentity(() -> {
try {
CharSequence callingAppLabel = mPm.getApplicationLabel(
@@ -1183,23 +1355,72 @@ public class ClipboardService extends SystemService {
String message =
getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
Slog.i(TAG, message);
- Toast toastToShow;
- if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
- Drawable safetyProtectionIcon = getContext()
- .getDrawable(R.drawable.ic_safety_protection);
- toastToShow = Toast.makeCustomToastWithIcon(getContext(),
- UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT, safetyProtectionIcon);
- } else {
- toastToShow = Toast.makeText(
- getContext(), UiThread.get().getLooper(), message,
- Toast.LENGTH_SHORT);
+ for (int i = 0; i < toastContexts.size(); i++) {
+ Context toastContext = toastContexts.valueAt(i);
+ Toast toastToShow;
+ if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
+ Drawable safetyProtectionIcon = getContext()
+ .getDrawable(R.drawable.ic_safety_protection);
+ toastToShow = Toast.makeCustomToastWithIcon(toastContext,
+ UiThread.get().getLooper(), message,
+ Toast.LENGTH_SHORT, safetyProtectionIcon);
+ } else {
+ toastToShow = Toast.makeText(
+ toastContext, UiThread.get().getLooper(), message,
+ Toast.LENGTH_SHORT);
+ }
+ toastToShow.show();
}
- toastToShow.show();
} catch (PackageManager.NameNotFoundException e) {
// do nothing
}
});
+
+ clipboard.mNotifiedUids.put(uid, true);
+ }
+
+ /**
+ * Returns the context(s) to use for toasts related to this clipboard. Normally this will just
+ * contain a single context referencing the default display.
+ *
+ * If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
+ * the focused VirtualDisplay for that device, but might need to return the contexts for
+ * multiple displays if the VirtualDevice has several but none of them were focused.
+ */
+ private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
+ ArraySet<Context> contexts = new ArraySet<>();
+
+ if (clipboard.deviceId != DEVICE_ID_DEFAULT) {
+ DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+
+ int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+ ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
+
+ if (displayIds.contains(topFocusedDisplayId)) {
+ Display display = displayManager.getDisplay(topFocusedDisplayId);
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ return contexts;
+ }
+ }
+
+ for (int i = 0; i < displayIds.size(); i++) {
+ Display display = displayManager.getDisplay(displayIds.valueAt(i));
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ }
+ }
+ if (!contexts.isEmpty()) {
+ return contexts;
+ }
+ Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
+ + clipboard.deviceId);
+ // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
+ // the default display below.
+ }
+
+ contexts.add(getContext());
+ return contexts;
}
/**
@@ -1221,7 +1442,7 @@ public class ClipboardService extends SystemService {
/** Potentially notifies the text classifier that an app is accessing a text clip. */
@GuardedBy("mLock")
private void notifyTextClassifierLocked(
- PerUserClipboard clipboard, String callingPackage, int callingUid) {
+ Clipboard clipboard, String callingPackage, int callingUid) {
if (clipboard.primaryClip == null) {
return;
}
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 974c04bc45f5..001cb10e1b29 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.companion.virtual.IVirtualDevice;
import android.os.LocaleList;
+import android.util.ArraySet;
import java.util.Set;
@@ -84,7 +85,7 @@ public abstract class VirtualDeviceManagerInternal {
* *Note* this only checks VirtualDevices, and does not include information about whether
* the app is running on the default device or not.
*/
- public abstract @NonNull Set<Integer> getDeviceIdsForUid(int uid);
+ public abstract @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid);
/**
* Notifies that a virtual display is created.
@@ -132,4 +133,12 @@ public abstract class VirtualDeviceManagerInternal {
* Returns true if the {@code displayId} is owned by any virtual device
*/
public abstract boolean isDisplayOwnedByAnyVirtualDevice(int displayId);
+
+ /**
+ * Gets the ids of VirtualDisplays owned by a VirtualDevice.
+ *
+ * @param deviceId which device we're asking about
+ * @return the set of display ids for all VirtualDisplays owned by the device
+ */
+ public abstract @NonNull ArraySet<Integer> getDisplayIdsForDevice(int deviceId);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index dad7977a8bb3..3791b3527b66 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -1595,6 +1595,33 @@ public class VirtualDeviceManagerServiceTest {
verify(mSoundEffectListener).onPlaySoundEffect(AudioManager.FX_KEY_CLICK);
}
+ @Test
+ public void getDisplayIdsForDevice_invalidDeviceId_emptyResult() {
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_2);
+ assertThat(displayIds).isEmpty();
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_noDisplays_emptyResult() {
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).isEmpty();
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_oneDisplay_resultContainsCorrectDisplayId() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).containsExactly(DISPLAY_ID_1);
+ }
+
+ @Test
+ public void getDisplayIdsForDevice_twoDisplays_resultContainsCorrectDisplayIds() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_2);
+ ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1);
+ assertThat(displayIds).containsExactly(DISPLAY_ID_1, DISPLAY_ID_2);
+ }
+
private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) {
VirtualDeviceParams params = new VirtualDeviceParams.Builder()
.setBlockedActivities(getBlockedActivities())