summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2021-04-09 21:53:30 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-04-09 21:53:30 +0000
commit6ac56f73559cec32bb11cb421d78f79d4b745c94 (patch)
tree40c9a278ce9e593cc04842674a86e9ade4c8c76e
parent5e543d378a2248b81d318832df3c62e61f90b212 (diff)
parent3ab180a990c4f7df81c159748254a59baa78d3b3 (diff)
downloadbase-temp_ab_7272582.tar.gz
Merge "Separate the emulator specific part of ClipboardService.java" am: ab750baa0c am: 3ab180a990temp_ab_7272582
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1663847 Change-Id: I981ec0a7542528ea00136a7e2ba18c2aa8a6d581
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java164
-rw-r--r--services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java168
-rw-r--r--services/core/java/com/android/server/clipboard/OWNERS1
3 files changed, 179 insertions, 154 deletions
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 72160c203595..f4e06d377a83 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -52,10 +52,6 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.VmSocketAddress;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,128 +63,9 @@ import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-
-// The following class is Android Emulator specific. It is used to read and
-// write contents of the host system's clipboard.
-class HostClipboardMonitor implements Runnable {
- public interface HostClipboardCallback {
- void onHostClipboardUpdated(String contents);
- }
-
- private FileDescriptor mPipe = null;
- private HostClipboardCallback mHostClipboardCallback;
- private static final String PIPE_NAME = "pipe:clipboard";
- private static final int HOST_PORT = 5000;
-
- private static byte[] createOpenHandshake() {
- // String.getBytes doesn't include the null terminator,
- // but the QEMU pipe device requires the pipe service name
- // to be null-terminated.
-
- final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
- bits[PIPE_NAME.length()] = 0;
- return bits;
- }
-
- private boolean openPipe() {
- try {
- final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
-
- try {
- Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
-
- final byte[] handshake = createOpenHandshake();
- Os.write(fd, handshake, 0, handshake.length);
- mPipe = fd;
- return true;
- } catch (ErrnoException | SocketException | InterruptedIOException e) {
- Os.close(fd);
- }
- } catch (ErrnoException e) {
- }
-
- return false;
- }
-
- private void closePipe() {
- try {
- final FileDescriptor fd = mPipe;
- mPipe = null;
- if (fd != null) {
- Os.close(fd);
- }
- } catch (ErrnoException ignore) {
- }
- }
-
- private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- Os.read(mPipe, lengthBits, 0, lengthBits.length);
-
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- final int msgLen = bb.getInt();
-
- final byte[] msg = new byte[msgLen];
- Os.read(mPipe, msg, 0, msg.length);
-
- return msg;
- }
-
- private void sendMessage(byte[] msg) throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- bb.putInt(msg.length);
-
- Os.write(mPipe, lengthBits, 0, lengthBits.length);
- Os.write(mPipe, msg, 0, msg.length);
- }
-
- public HostClipboardMonitor(HostClipboardCallback cb) {
- mHostClipboardCallback = cb;
- }
-
- @Override
- public void run() {
- while (!Thread.interrupted()) {
- try {
- // There's no guarantee that QEMU pipes will be ready at the moment
- // this method is invoked. We simply try to get the pipe open and
- // retry on failure indefinitely.
- while ((mPipe == null) && !openPipe()) {
- Thread.sleep(100);
- }
-
- final byte[] receivedData = receiveMessage();
- mHostClipboardCallback.onHostClipboardUpdated(
- new String(receivedData));
- } catch (ErrnoException | InterruptedIOException e) {
- closePipe();
- } catch (InterruptedException e) {
- }
- }
- }
-
- public void setHostClipboard(String content) {
- try {
- if (mPipe != null) {
- sendMessage(content.getBytes());
- }
- } catch (ErrnoException | InterruptedIOException e) {
- Slog.e("HostClipboardMonitor",
- "Failed to set host clipboard " + e.getMessage());
- }
- }
-}
+import java.util.function.Consumer;
/**
* Implementation of the clipboard for copy and paste.
@@ -214,8 +91,7 @@ public class ClipboardService extends SystemService {
private final ContentCaptureManagerInternal mContentCaptureInternal;
private final AutofillManagerInternal mAutofillInternal;
private final IBinder mPermissionOwner;
- private HostClipboardMonitor mHostClipboardMonitor = null;
- private Thread mHostMonitorThread = null;
+ private final Consumer<ClipData> mEmulatorClipboardMonitor;
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -237,22 +113,13 @@ public class ClipboardService extends SystemService {
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
- mHostClipboardMonitor = new HostClipboardMonitor(
- new HostClipboardMonitor.HostClipboardCallback() {
- @Override
- public void onHostClipboardUpdated(String contents){
- ClipData clip =
- new ClipData("host clipboard",
- new String[]{"text/plain"},
- new ClipData.Item(contents));
- synchronized(mClipboards) {
- setPrimaryClipInternal(getClipboard(0), clip,
- android.os.Process.SYSTEM_UID);
- }
- }
- });
- mHostMonitorThread = new Thread(mHostClipboardMonitor);
- mHostMonitorThread.start();
+ mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+ synchronized (this) {
+ setPrimaryClipInternal(getClipboard(0), clip, android.os.Process.SYSTEM_UID);
+ }
+ });
+ } else {
+ mEmulatorClipboardMonitor = (clip) -> {};
}
}
@@ -547,18 +414,7 @@ public class ClipboardService extends SystemService {
}
void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
- // Push clipboard to host, if any
- if (mHostClipboardMonitor != null) {
- if (clip == null) {
- // Someone really wants the clipboard cleared, so push empty
- mHostClipboardMonitor.setHostClipboard("");
- } else if (clip.getItemCount() > 0) {
- final CharSequence text = clip.getItemAt(0).getText();
- if (text != null) {
- mHostClipboardMonitor.setHostClipboard(text.toString());
- }
- }
- }
+ mEmulatorClipboardMonitor.accept(clip);
// Update this user
final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
new file mode 100644
index 000000000000..62b701aff398
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 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.annotation.Nullable;
+import android.content.ClipData;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.VmSocketAddress;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class EmulatorClipboardMonitor implements Consumer<ClipData> {
+ private static final String TAG = "EmulatorClipboardMonitor";
+ private static final String PIPE_NAME = "pipe:clipboard";
+ private static final int HOST_PORT = 5000;
+ private final Thread mHostMonitorThread;
+ private FileDescriptor mPipe = null;
+
+ private static byte[] createOpenHandshake() {
+ // String.getBytes doesn't include the null terminator,
+ // but the QEMU pipe device requires the pipe service name
+ // to be null-terminated.
+
+ final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+ bits[PIPE_NAME.length()] = 0;
+ return bits;
+ }
+
+ private boolean isPipeOpened() {
+ return mPipe != null;
+ }
+
+ private synchronized boolean openPipe() {
+ if (mPipe != null) {
+ return true;
+ }
+
+ try {
+ final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
+
+ try {
+ Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
+
+ final byte[] handshake = createOpenHandshake();
+ Os.write(fd, handshake, 0, handshake.length);
+ mPipe = fd;
+ return true;
+ } catch (ErrnoException | SocketException | InterruptedIOException e) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException e) {
+ }
+
+ return false;
+ }
+
+ private synchronized void closePipe() {
+ try {
+ final FileDescriptor fd = mPipe;
+ mPipe = null;
+ if (fd != null) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException ignore) {
+ }
+ }
+
+ private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ Os.read(mPipe, lengthBits, 0, lengthBits.length);
+
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ final int msgLen = bb.getInt();
+
+ final byte[] msg = new byte[msgLen];
+ Os.read(mPipe, msg, 0, msg.length);
+
+ return msg;
+ }
+
+ private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ bb.putInt(msg.length);
+
+ Os.write(mPipe, lengthBits, 0, lengthBits.length);
+ Os.write(mPipe, msg, 0, msg.length);
+ }
+
+ EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
+ this.mHostMonitorThread = new Thread(() -> {
+ while (!Thread.interrupted()) {
+ try {
+ // There's no guarantee that QEMU pipes will be ready at the moment
+ // this method is invoked. We simply try to get the pipe open and
+ // retry on failure indefinitely.
+ while (!openPipe()) {
+ Thread.sleep(100);
+ }
+
+ final byte[] receivedData = receiveMessage();
+
+ final String str = new String(receivedData);
+ final ClipData clip = new ClipData("host clipboard",
+ new String[]{"text/plain"},
+ new ClipData.Item(str));
+
+ setAndroidClipboard.accept(clip);
+ } catch (ErrnoException | InterruptedIOException e) {
+ closePipe();
+ } catch (InterruptedException | IllegalArgumentException e) {
+ }
+ }
+ });
+
+ this.mHostMonitorThread.start();
+ }
+
+ @Override
+ public void accept(final @Nullable ClipData clip) {
+ if (clip == null) {
+ setHostClipboardImpl("");
+ } else if (clip.getItemCount() > 0) {
+ final CharSequence text = clip.getItemAt(0).getText();
+ if (text != null) {
+ setHostClipboardImpl(text.toString());
+ }
+ }
+ }
+
+ private void setHostClipboardImpl(final String value) {
+ try {
+ if (isPipeOpened()) {
+ sendMessage(value.getBytes());
+ }
+ } catch (ErrnoException | InterruptedIOException e) {
+ Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
new file mode 100644
index 000000000000..5449df908051
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -0,0 +1 @@
+per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com