Skip to content

Commit 03d2f29

Browse files
Amith YamasaniAndroid (Google) Code Review
authored andcommitted
Merge "Clipboard service keeps separate clipboards per user."
2 parents 1579a67 + e9e26cc commit 03d2f29

File tree

1 file changed

+80
-25
lines changed

1 file changed

+80
-25
lines changed

services/java/com/android/server/ClipboardService.java

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
import android.app.ActivityManagerNative;
2020
import android.app.IActivityManager;
21+
import android.content.BroadcastReceiver;
2122
import android.content.ClipData;
2223
import android.content.ClipDescription;
2324
import android.content.IClipboard;
2425
import android.content.IOnPrimaryClipChangedListener;
2526
import android.content.Context;
2627
import android.content.Intent;
28+
import android.content.IntentFilter;
2729
import android.content.pm.PackageInfo;
2830
import android.content.pm.PackageManager;
2931
import android.content.pm.PackageManager.NameNotFoundException;
@@ -37,25 +39,39 @@
3739
import android.os.UserId;
3840
import android.util.Pair;
3941
import android.util.Slog;
42+
import android.util.SparseArray;
4043

4144
import java.util.HashSet;
4245

4346
/**
4447
* Implementation of the clipboard for copy and paste.
4548
*/
4649
public class ClipboardService extends IClipboard.Stub {
50+
51+
private static final String TAG = "ClipboardService";
52+
4753
private final Context mContext;
4854
private final IActivityManager mAm;
4955
private final PackageManager mPm;
5056
private final IBinder mPermissionOwner;
5157

52-
private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
53-
= new RemoteCallbackList<IOnPrimaryClipChangedListener>();
58+
private class PerUserClipboard {
59+
final int userId;
60+
61+
final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
62+
= new RemoteCallbackList<IOnPrimaryClipChangedListener>();
63+
64+
ClipData primaryClip;
65+
66+
final HashSet<String> activePermissionOwners
67+
= new HashSet<String>();
5468

55-
private ClipData mPrimaryClip;
69+
PerUserClipboard(int userId) {
70+
this.userId = userId;
71+
}
72+
}
5673

57-
private final HashSet<String> mActivePermissionOwners
58-
= new HashSet<String>();
74+
private SparseArray<PerUserClipboard> mClipboards = new SparseArray<PerUserClipboard>();
5975

6076
/**
6177
* Instantiates the clipboard.
@@ -71,6 +87,19 @@ public ClipboardService(Context context) {
7187
Slog.w("clipboard", "AM dead", e);
7288
}
7389
mPermissionOwner = permOwner;
90+
91+
// Remove the clipboard if a user is removed
92+
IntentFilter userFilter = new IntentFilter();
93+
userFilter.addAction(Intent.ACTION_USER_REMOVED);
94+
mContext.registerReceiver(new BroadcastReceiver() {
95+
@Override
96+
public void onReceive(Context context, Intent intent) {
97+
String action = intent.getAction();
98+
if (Intent.ACTION_USER_REMOVED.equals(action)) {
99+
removeClipboard(intent.getIntExtra(Intent.EXTRA_USERID, 0));
100+
}
101+
}
102+
}, userFilter);
74103
}
75104

76105
@Override
@@ -85,63 +114,88 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
85114

86115
}
87116

117+
private PerUserClipboard getClipboard() {
118+
return getClipboard(UserId.getCallingUserId());
119+
}
120+
121+
private PerUserClipboard getClipboard(int userId) {
122+
synchronized (mClipboards) {
123+
Slog.i(TAG, "Got clipboard for user=" + userId);
124+
PerUserClipboard puc = mClipboards.get(userId);
125+
if (puc == null) {
126+
puc = new PerUserClipboard(userId);
127+
mClipboards.put(userId, puc);
128+
}
129+
return puc;
130+
}
131+
}
132+
133+
private void removeClipboard(int userId) {
134+
synchronized (mClipboards) {
135+
mClipboards.remove(userId);
136+
}
137+
}
138+
88139
public void setPrimaryClip(ClipData clip) {
89140
synchronized (this) {
90141
if (clip != null && clip.getItemCount() <= 0) {
91142
throw new IllegalArgumentException("No items");
92143
}
93144
checkDataOwnerLocked(clip, Binder.getCallingUid());
94145
clearActiveOwnersLocked();
95-
mPrimaryClip = clip;
96-
final int n = mPrimaryClipListeners.beginBroadcast();
146+
PerUserClipboard clipboard = getClipboard();
147+
clipboard.primaryClip = clip;
148+
final int n = clipboard.primaryClipListeners.beginBroadcast();
97149
for (int i = 0; i < n; i++) {
98150
try {
99-
mPrimaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();
151+
clipboard.primaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();
100152
} catch (RemoteException e) {
101153

102154
// The RemoteCallbackList will take care of removing
103155
// the dead object for us.
104156
}
105157
}
106-
mPrimaryClipListeners.finishBroadcast();
158+
clipboard.primaryClipListeners.finishBroadcast();
107159
}
108160
}
109161

110162
public ClipData getPrimaryClip(String pkg) {
111163
synchronized (this) {
112164
addActiveOwnerLocked(Binder.getCallingUid(), pkg);
113-
return mPrimaryClip;
165+
return getClipboard().primaryClip;
114166
}
115167
}
116168

117169
public ClipDescription getPrimaryClipDescription() {
118170
synchronized (this) {
119-
return mPrimaryClip != null ? mPrimaryClip.getDescription() : null;
171+
PerUserClipboard clipboard = getClipboard();
172+
return clipboard.primaryClip != null ? clipboard.primaryClip.getDescription() : null;
120173
}
121174
}
122175

123176
public boolean hasPrimaryClip() {
124177
synchronized (this) {
125-
return mPrimaryClip != null;
178+
return getClipboard().primaryClip != null;
126179
}
127180
}
128181

129182
public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
130183
synchronized (this) {
131-
mPrimaryClipListeners.register(listener);
184+
getClipboard().primaryClipListeners.register(listener);
132185
}
133186
}
134187

135188
public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
136189
synchronized (this) {
137-
mPrimaryClipListeners.unregister(listener);
190+
getClipboard().primaryClipListeners.unregister(listener);
138191
}
139192
}
140193

141194
public boolean hasClipboardText() {
142195
synchronized (this) {
143-
if (mPrimaryClip != null) {
144-
CharSequence text = mPrimaryClip.getItemAt(0).getText();
196+
PerUserClipboard clipboard = getClipboard();
197+
if (clipboard.primaryClip != null) {
198+
CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
145199
return text != null && text.length() > 0;
146200
}
147201
return false;
@@ -153,7 +207,6 @@ private final void checkUriOwnerLocked(Uri uri, int uid) {
153207
return;
154208
}
155209
long ident = Binder.clearCallingIdentity();
156-
boolean allowed = false;
157210
try {
158211
// This will throw SecurityException for us.
159212
mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -212,12 +265,13 @@ private final void addActiveOwnerLocked(int uid, String pkg) {
212265
} catch (NameNotFoundException e) {
213266
throw new IllegalArgumentException("Unknown package " + pkg, e);
214267
}
215-
if (mPrimaryClip != null && !mActivePermissionOwners.contains(pkg)) {
216-
final int N = mPrimaryClip.getItemCount();
268+
PerUserClipboard clipboard = getClipboard();
269+
if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
270+
final int N = clipboard.primaryClip.getItemCount();
217271
for (int i=0; i<N; i++) {
218-
grantItemLocked(mPrimaryClip.getItemAt(i), pkg);
272+
grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg);
219273
}
220-
mActivePermissionOwners.add(pkg);
274+
clipboard.activePermissionOwners.add(pkg);
221275
}
222276
}
223277

@@ -244,13 +298,14 @@ private final void revokeItemLocked(ClipData.Item item) {
244298
}
245299

246300
private final void clearActiveOwnersLocked() {
247-
mActivePermissionOwners.clear();
248-
if (mPrimaryClip == null) {
301+
PerUserClipboard clipboard = getClipboard();
302+
clipboard.activePermissionOwners.clear();
303+
if (clipboard.primaryClip == null) {
249304
return;
250305
}
251-
final int N = mPrimaryClip.getItemCount();
306+
final int N = clipboard.primaryClip.getItemCount();
252307
for (int i=0; i<N; i++) {
253-
revokeItemLocked(mPrimaryClip.getItemAt(i));
308+
revokeItemLocked(clipboard.primaryClip.getItemAt(i));
254309
}
255310
}
256311
}

0 commit comments

Comments
 (0)