Merge "VirtualDevice clipboard support"
This commit is contained in:
committed by
Android (Google) Code Review
commit
500c7dd5ac
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -586,6 +585,20 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
return false;
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +384,54 @@ public class ClipboardService extends SystemService {
|
||||
UserHandle.getAppId(Binder.getCallingUid()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user