summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/clipboard
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2013-11-22 08:25:26 -0800
committerAmith Yamasani <yamasani@google.com>2013-12-19 15:25:37 -0800
commit9158825f9c41869689d6b1786d7c7aa8bdd524ce (patch)
treef41944461539f0c70030668b4558296469c307d3 /services/core/java/com/android/server/clipboard
parent30d032928a294fbb6f385e9d0367a75b7bf2649b (diff)
downloadframeworks_base-9158825f9c41869689d6b1786d7c7aa8bdd524ce.zip
frameworks_base-9158825f9c41869689d6b1786d7c7aa8bdd524ce.tar.gz
frameworks_base-9158825f9c41869689d6b1786d7c7aa8bdd524ce.tar.bz2
Move some system services to separate directories
Refactored the directory structure so that services can be optionally excluded. This is step 1. Will be followed by another change that makes it possible to remove services from the build. Change-Id: Ideacedfd34b5e213217ad3ff4ebb21c4a8e73f85
Diffstat (limited to 'services/core/java/com/android/server/clipboard')
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java366
1 files changed, 366 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
new file mode 100644
index 0000000..6aa596d
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.clipboard;
+
+import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.IClipboard;
+import android.content.IOnPrimaryClipChangedListener;
+import android.content.Context;
+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.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.HashSet;
+
+/**
+ * Implementation of the clipboard for copy and paste.
+ */
+public class ClipboardService extends IClipboard.Stub {
+
+ private static final String TAG = "ClipboardService";
+
+ private final Context mContext;
+ private final IActivityManager mAm;
+ private final PackageManager mPm;
+ private final AppOpsManager mAppOps;
+ private final IBinder mPermissionOwner;
+
+ private class ListenerInfo {
+ final int mUid;
+ final String mPackageName;
+ ListenerInfo(int uid, String packageName) {
+ mUid = uid;
+ mPackageName = packageName;
+ }
+ }
+
+ private class PerUserClipboard {
+ final int userId;
+
+ final RemoteCallbackList<IOnPrimaryClipChangedListener> primaryClipListeners
+ = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
+
+ ClipData primaryClip;
+
+ final HashSet<String> activePermissionOwners
+ = new HashSet<String>();
+
+ PerUserClipboard(int userId) {
+ this.userId = userId;
+ }
+ }
+
+ private SparseArray<PerUserClipboard> mClipboards = new SparseArray<PerUserClipboard>();
+
+ /**
+ * Instantiates the clipboard.
+ */
+ public ClipboardService(Context context) {
+ mContext = context;
+ mAm = ActivityManagerNative.getDefault();
+ mPm = context.getPackageManager();
+ mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ IBinder permOwner = null;
+ try {
+ permOwner = mAm.newUriPermissionOwner("clipboard");
+ } catch (RemoteException e) {
+ Slog.w("clipboard", "AM dead", e);
+ }
+ mPermissionOwner = permOwner;
+
+ // Remove the clipboard if a user is removed
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
+ }
+ }, userFilter);
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (RuntimeException e) {
+ if (!(e instanceof SecurityException)) {
+ Slog.wtf("clipboard", "Exception: ", e);
+ }
+ throw e;
+ }
+
+ }
+
+ private PerUserClipboard getClipboard() {
+ return getClipboard(UserHandle.getCallingUserId());
+ }
+
+ private PerUserClipboard getClipboard(int userId) {
+ synchronized (mClipboards) {
+ PerUserClipboard puc = mClipboards.get(userId);
+ if (puc == null) {
+ puc = new PerUserClipboard(userId);
+ mClipboards.put(userId, puc);
+ }
+ return puc;
+ }
+ }
+
+ private void removeClipboard(int userId) {
+ synchronized (mClipboards) {
+ mClipboards.remove(userId);
+ }
+ }
+
+ public void setPrimaryClip(ClipData clip, String callingPackage) {
+ synchronized (this) {
+ if (clip != null && clip.getItemCount() <= 0) {
+ throw new IllegalArgumentException("No items");
+ }
+ final int callingUid = Binder.getCallingUid();
+ if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid,
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+ checkDataOwnerLocked(clip, callingUid);
+ clearActiveOwnersLocked();
+ PerUserClipboard clipboard = getClipboard();
+ clipboard.primaryClip = clip;
+ final long ident = Binder.clearCallingIdentity();
+ final int n = clipboard.primaryClipListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ try {
+ ListenerInfo li = (ListenerInfo)
+ clipboard.primaryClipListeners.getBroadcastCookie(i);
+ if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid,
+ li.mPackageName) == AppOpsManager.MODE_ALLOWED) {
+ clipboard.primaryClipListeners.getBroadcastItem(i)
+ .dispatchPrimaryClipChanged();
+ }
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ } finally {
+ clipboard.primaryClipListeners.finishBroadcast();
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public ClipData getPrimaryClip(String pkg) {
+ synchronized (this) {
+ if (mAppOps.noteOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+ pkg) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ addActiveOwnerLocked(Binder.getCallingUid(), pkg);
+ return getClipboard().primaryClip;
+ }
+ }
+
+ public ClipDescription getPrimaryClipDescription(String callingPackage) {
+ synchronized (this) {
+ if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return null;
+ }
+ PerUserClipboard clipboard = getClipboard();
+ return clipboard.primaryClip != null ? clipboard.primaryClip.getDescription() : null;
+ }
+ }
+
+ public boolean hasPrimaryClip(String callingPackage) {
+ synchronized (this) {
+ if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ return getClipboard().primaryClip != null;
+ }
+ }
+
+ public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
+ String callingPackage) {
+ synchronized (this) {
+ getClipboard().primaryClipListeners.register(listener,
+ new ListenerInfo(Binder.getCallingUid(), callingPackage));
+ }
+ }
+
+ public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+ synchronized (this) {
+ getClipboard().primaryClipListeners.unregister(listener);
+ }
+ }
+
+ public boolean hasClipboardText(String callingPackage) {
+ synchronized (this) {
+ if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ PerUserClipboard clipboard = getClipboard();
+ if (clipboard.primaryClip != null) {
+ CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
+ return text != null && text.length() > 0;
+ }
+ return false;
+ }
+ }
+
+ private final void checkUriOwnerLocked(Uri uri, int uid) {
+ if (!"content".equals(uri.getScheme())) {
+ return;
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ // This will throw SecurityException for us.
+ mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void checkItemOwnerLocked(ClipData.Item item, int uid) {
+ if (item.getUri() != null) {
+ checkUriOwnerLocked(item.getUri(), uid);
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ checkUriOwnerLocked(intent.getData(), uid);
+ }
+ }
+
+ private final void checkDataOwnerLocked(ClipData data, int uid) {
+ final int N = data.getItemCount();
+ for (int i=0; i<N; i++) {
+ checkItemOwnerLocked(data.getItemAt(i), uid);
+ }
+ }
+
+ private final void grantUriLocked(Uri uri, String pkg) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void grantItemLocked(ClipData.Item item, String pkg) {
+ if (item.getUri() != null) {
+ grantUriLocked(item.getUri(), pkg);
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ grantUriLocked(intent.getData(), pkg);
+ }
+ }
+
+ private final void addActiveOwnerLocked(int uid, String pkg) {
+ final IPackageManager pm = AppGlobals.getPackageManager();
+ final int targetUserHandle = UserHandle.getCallingUserId();
+ final long oldIdentity = Binder.clearCallingIdentity();
+ try {
+ PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle);
+ if (pi == null) {
+ throw new IllegalArgumentException("Unknown package " + pkg);
+ }
+ if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
+ throw new SecurityException("Calling uid " + uid
+ + " does not own package " + pkg);
+ }
+ } catch (RemoteException e) {
+ // Can't happen; the package manager is in the same process
+ } finally {
+ Binder.restoreCallingIdentity(oldIdentity);
+ }
+ PerUserClipboard clipboard = getClipboard();
+ if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) {
+ final int N = clipboard.primaryClip.getItemCount();
+ for (int i=0; i<N; i++) {
+ grantItemLocked(clipboard.primaryClip.getItemAt(i), pkg);
+ }
+ clipboard.activePermissionOwners.add(pkg);
+ }
+ }
+
+ private final void revokeUriLocked(Uri uri) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private final void revokeItemLocked(ClipData.Item item) {
+ if (item.getUri() != null) {
+ revokeUriLocked(item.getUri());
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ revokeUriLocked(intent.getData());
+ }
+ }
+
+ private final void clearActiveOwnersLocked() {
+ PerUserClipboard clipboard = getClipboard();
+ clipboard.activePermissionOwners.clear();
+ if (clipboard.primaryClip == null) {
+ return;
+ }
+ final int N = clipboard.primaryClip.getItemCount();
+ for (int i=0; i<N; i++) {
+ revokeItemLocked(clipboard.primaryClip.getItemAt(i));
+ }
+ }
+}