blob: 2e2a278eb4b26d13fc9c20914bdb34144c370653 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070019import android.app.ActivityManagerNative;
20import android.app.IActivityManager;
Dianne Hackborn1040dc42010-08-26 22:11:06 -070021import android.content.ClipData;
22import android.content.ClipDescription;
Dianne Hackborn9f531192010-08-04 17:48:03 -070023import android.content.IClipboard;
24import android.content.IOnPrimaryClipChangedListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070026import android.content.Intent;
27import android.content.pm.PackageInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.net.Uri;
31import android.os.Binder;
32import android.os.IBinder;
33import android.os.Parcel;
34import android.os.Process;
Dianne Hackborn9f531192010-08-04 17:48:03 -070035import android.os.RemoteCallbackList;
36import android.os.RemoteException;
Amith Yamasani13593602012-03-22 16:16:17 -070037import android.os.UserId;
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070038import android.util.Pair;
39import android.util.Slog;
40
41import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43/**
44 * Implementation of the clipboard for copy and paste.
45 */
46public class ClipboardService extends IClipboard.Stub {
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070047 private final Context mContext;
48 private final IActivityManager mAm;
49 private final PackageManager mPm;
50 private final IBinder mPermissionOwner;
51
Dianne Hackborn9f531192010-08-04 17:48:03 -070052 private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
53 = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070055 private ClipData mPrimaryClip;
56
57 private final HashSet<String> mActivePermissionOwners
58 = new HashSet<String>();
59
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 /**
61 * Instantiates the clipboard.
62 */
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070063 public ClipboardService(Context context) {
64 mContext = context;
65 mAm = ActivityManagerNative.getDefault();
66 mPm = context.getPackageManager();
67 IBinder permOwner = null;
68 try {
69 permOwner = mAm.newUriPermissionOwner("clipboard");
70 } catch (RemoteException e) {
71 Slog.w("clipboard", "AM dead", e);
72 }
73 mPermissionOwner = permOwner;
74 }
75
76 @Override
77 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
78 throws RemoteException {
79 try {
80 return super.onTransact(code, data, reply, flags);
81 } catch (RuntimeException e) {
82 Slog.w("clipboard", "Exception: ", e);
83 throw e;
84 }
85
86 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
Dianne Hackborn1040dc42010-08-26 22:11:06 -070088 public void setPrimaryClip(ClipData clip) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 synchronized (this) {
Dianne Hackborn9f531192010-08-04 17:48:03 -070090 if (clip != null && clip.getItemCount() <= 0) {
91 throw new IllegalArgumentException("No items");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 }
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -070093 checkDataOwnerLocked(clip, Binder.getCallingUid());
94 clearActiveOwnersLocked();
Dianne Hackborn9f531192010-08-04 17:48:03 -070095 mPrimaryClip = clip;
96 final int n = mPrimaryClipListeners.beginBroadcast();
97 for (int i = 0; i < n; i++) {
98 try {
99 mPrimaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();
100 } catch (RemoteException e) {
101
102 // The RemoteCallbackList will take care of removing
103 // the dead object for us.
104 }
105 }
106 mPrimaryClipListeners.finishBroadcast();
107 }
108 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700110 public ClipData getPrimaryClip(String pkg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 synchronized (this) {
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700112 addActiveOwnerLocked(Binder.getCallingUid(), pkg);
Dianne Hackborn9f531192010-08-04 17:48:03 -0700113 return mPrimaryClip;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 }
115 }
116
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700117 public ClipDescription getPrimaryClipDescription() {
118 synchronized (this) {
Dianne Hackborn3b99ede2010-12-22 17:37:34 -0800119 return mPrimaryClip != null ? mPrimaryClip.getDescription() : null;
Dianne Hackborn1040dc42010-08-26 22:11:06 -0700120 }
121 }
122
Dianne Hackborn9f531192010-08-04 17:48:03 -0700123 public boolean hasPrimaryClip() {
124 synchronized (this) {
125 return mPrimaryClip != null;
126 }
127 }
128
129 public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
130 synchronized (this) {
131 mPrimaryClipListeners.register(listener);
132 }
133 }
134
135 public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
136 synchronized (this) {
137 mPrimaryClipListeners.unregister(listener);
138 }
139 }
140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 public boolean hasClipboardText() {
142 synchronized (this) {
Dianne Hackborn9f531192010-08-04 17:48:03 -0700143 if (mPrimaryClip != null) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800144 CharSequence text = mPrimaryClip.getItemAt(0).getText();
Dianne Hackborn9f531192010-08-04 17:48:03 -0700145 return text != null && text.length() > 0;
146 }
147 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 }
149 }
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700150
151 private final void checkUriOwnerLocked(Uri uri, int uid) {
152 if (!"content".equals(uri.getScheme())) {
153 return;
154 }
155 long ident = Binder.clearCallingIdentity();
156 boolean allowed = false;
157 try {
158 // This will throw SecurityException for us.
159 mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
160 } catch (RemoteException e) {
161 } finally {
162 Binder.restoreCallingIdentity(ident);
163 }
164 }
165
166 private final void checkItemOwnerLocked(ClipData.Item item, int uid) {
167 if (item.getUri() != null) {
168 checkUriOwnerLocked(item.getUri(), uid);
169 }
170 Intent intent = item.getIntent();
171 if (intent != null && intent.getData() != null) {
172 checkUriOwnerLocked(intent.getData(), uid);
173 }
174 }
175
176 private final void checkDataOwnerLocked(ClipData data, int uid) {
177 final int N = data.getItemCount();
178 for (int i=0; i<N; i++) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800179 checkItemOwnerLocked(data.getItemAt(i), uid);
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700180 }
181 }
182
183 private final void grantUriLocked(Uri uri, String pkg) {
184 long ident = Binder.clearCallingIdentity();
185 try {
186 mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
187 Intent.FLAG_GRANT_READ_URI_PERMISSION);
188 } catch (RemoteException e) {
189 } finally {
190 Binder.restoreCallingIdentity(ident);
191 }
192 }
193
194 private final void grantItemLocked(ClipData.Item item, String pkg) {
195 if (item.getUri() != null) {
196 grantUriLocked(item.getUri(), pkg);
197 }
198 Intent intent = item.getIntent();
199 if (intent != null && intent.getData() != null) {
200 grantUriLocked(intent.getData(), pkg);
201 }
202 }
203
204 private final void addActiveOwnerLocked(int uid, String pkg) {
205 PackageInfo pi;
206 try {
207 pi = mPm.getPackageInfo(pkg, 0);
Amith Yamasani13593602012-03-22 16:16:17 -0700208 if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700209 throw new SecurityException("Calling uid " + uid
210 + " does not own package " + pkg);
211 }
212 } catch (NameNotFoundException e) {
213 throw new IllegalArgumentException("Unknown package " + pkg, e);
214 }
Dianne Hackborn3b99ede2010-12-22 17:37:34 -0800215 if (mPrimaryClip != null && !mActivePermissionOwners.contains(pkg)) {
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700216 final int N = mPrimaryClip.getItemCount();
217 for (int i=0; i<N; i++) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800218 grantItemLocked(mPrimaryClip.getItemAt(i), pkg);
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700219 }
220 mActivePermissionOwners.add(pkg);
221 }
222 }
223
224 private final void revokeUriLocked(Uri uri) {
225 long ident = Binder.clearCallingIdentity();
226 try {
227 mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
228 Intent.FLAG_GRANT_READ_URI_PERMISSION
229 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
230 } catch (RemoteException e) {
231 } finally {
232 Binder.restoreCallingIdentity(ident);
233 }
234 }
235
236 private final void revokeItemLocked(ClipData.Item item) {
237 if (item.getUri() != null) {
238 revokeUriLocked(item.getUri());
239 }
240 Intent intent = item.getIntent();
241 if (intent != null && intent.getData() != null) {
242 revokeUriLocked(intent.getData());
243 }
244 }
245
246 private final void clearActiveOwnersLocked() {
247 mActivePermissionOwners.clear();
248 if (mPrimaryClip == null) {
249 return;
250 }
251 final int N = mPrimaryClip.getItemCount();
252 for (int i=0; i<N; i++) {
Dianne Hackborn327fbd22011-01-17 14:38:50 -0800253 revokeItemLocked(mPrimaryClip.getItemAt(i));
Dianne Hackborn90f4aaf2010-09-27 14:58:44 -0700254 }
255 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256}