1818
1919import android .app .ActivityManagerNative ;
2020import android .app .IActivityManager ;
21+ import android .content .BroadcastReceiver ;
2122import android .content .ClipData ;
2223import android .content .ClipDescription ;
2324import android .content .IClipboard ;
2425import android .content .IOnPrimaryClipChangedListener ;
2526import android .content .Context ;
2627import android .content .Intent ;
28+ import android .content .IntentFilter ;
2729import android .content .pm .PackageInfo ;
2830import android .content .pm .PackageManager ;
2931import android .content .pm .PackageManager .NameNotFoundException ;
3739import android .os .UserId ;
3840import android .util .Pair ;
3941import android .util .Slog ;
42+ import android .util .SparseArray ;
4043
4144import java .util .HashSet ;
4245
4346/**
4447 * Implementation of the clipboard for copy and paste.
4548 */
4649public 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