diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-04-09 21:53:30 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-04-09 21:53:30 +0000 |
commit | 6ac56f73559cec32bb11cb421d78f79d4b745c94 (patch) | |
tree | 40c9a278ce9e593cc04842674a86e9ade4c8c76e | |
parent | 5e543d378a2248b81d318832df3c62e61f90b212 (diff) | |
parent | 3ab180a990c4f7df81c159748254a59baa78d3b3 (diff) | |
download | base-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
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 |