summaryrefslogtreecommitdiffstats
path: root/components/devtools_bridge/android
diff options
context:
space:
mode:
authorphoglund <phoglund@chromium.org>2014-09-24 00:32:58 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-24 07:33:14 +0000
commitcf0900dae461d90af8ed7604da098690d7c744c3 (patch)
tree49f818e6b5144cc417c5fc989118879e34786c37 /components/devtools_bridge/android
parent15044609893faf9b2c2a533aa9a136f288ea20a1 (diff)
downloadchromium_src-cf0900dae461d90af8ed7604da098690d7c744c3.zip
chromium_src-cf0900dae461d90af8ed7604da098690d7c744c3.tar.gz
chromium_src-cf0900dae461d90af8ed7604da098690d7c744c3.tar.bz2
Revert of DevTools socket tunnel. (patchset #11 id:300001 of https://codereview.chromium.org/517233002/)
Reason for revert: Fails to compile on Android: https://build.chromium.org/p/chromium.linux/builders/Android%20Arm64%20Builder%20(dbg)/builds/2163 devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java:291: cannot find symbol symbol: method sleep() sleep(); ^ Original issue's description: > Implementation of DevTools socket tunneling service for remote debugging. > > Test APK in addition to instrumentation tests contains launching activity and foreground service what let duplicate ChromeShell debug socket to a differently named socket. If a device with running ChromeShell and DevToolsBridgeTest's service connect to a Desktop chrome (with USB debugging enabled) then chrome://inspect#devices will show 2 ChromeChell items. Both are debuggable (https://codereview.chromium.org/521573002/ should be applied to Desktop chrome to make the process less surprising). It's suitable for manual tests. > > This CL doesn't care of a few known issues: > 1. Data channel buffer overflow. Data channel automatically closed in this case. > 2. Uncontrolled number of pending sockets. DevTools throttles opening sockets and and keeps sockets that exceeds a threshold hanging. > 3. Uncontrolled number of threads (it's actually not a problem if #2 solved, thanks to DevTools throttling). > > To keep this CL reasonably simple this issues will be addressed later. > > TEST=org.chromium.components.devtools_bridge.SocketTunnelServerTest, see description fro manual testing. > BUG=383418 > > Committed: https://crrev.com/15044609893faf9b2c2a533aa9a136f288ea20a1 > Cr-Commit-Position: refs/heads/master@{#296360} TBR=mnaganov@chromium.org,tedchoc@chromium.org,erikwright@chromium.org,serya@chromium.org NOTREECHECKS=true NOTRY=true BUG=383418 Review URL: https://codereview.chromium.org/597063002 Cr-Commit-Position: refs/heads/master@{#296361}
Diffstat (limited to 'components/devtools_bridge/android')
-rw-r--r--components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/AbstractDataChannel.java77
-rw-r--r--components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelBase.java396
-rw-r--r--components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelServer.java135
-rw-r--r--components/devtools_bridge/android/javatests/AndroidManifest.xml36
-rw-r--r--components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridgeTest.java111
-rw-r--r--components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java308
-rw-r--r--components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugActivity.java71
-rw-r--r--components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugService.java111
8 files changed, 0 insertions, 1245 deletions
diff --git a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/AbstractDataChannel.java b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/AbstractDataChannel.java
deleted file mode 100644
index e92c6db..0000000
--- a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/AbstractDataChannel.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge;
-
-import java.nio.ByteBuffer;
-
-/**
- * Limited view on org.webrtc.DataChannel. Abstraction layer helps with:
- * 1. Mocking in tests. There is no need to emulate full set of features of the DataChannel.
- * 2. Allows both native and Java API implementation for WebRTC data channel.
- * 3. Hides unused features.
- * Only SCTP data channels supported.
- * Data channel is thread safe (except the dispose method).
- */
-public abstract class AbstractDataChannel {
- /**
- * Observer's callbacks are called on WebRTC signaling thread (or it's equivalent in tests).
- */
- public interface Observer {
- void onStateChange(State state);
-
- /**
- * TEXT and BINARY messages should be handled equally. Size of the message is
- * |message|.remaining(). |message| may reference to a native buffer on stack so
- * the reference to the buffer must not outlive the invocation.
- */
- void onMessage(ByteBuffer message);
- }
-
- /**
- * Type is only significant for JavaScript-based counterpart. TEXT messages will
- * be observed as strings, BINARY as ByteArray's.
- */
- public enum MessageType {
- TEXT, BINARY
- }
-
- /**
- * State of the data channel.
- * Only 2 states of channel are important here: OPEN and everything else.
- */
- public enum State {
- OPEN, CLOSED
- }
-
- /**
- * Registers an observer.
- */
- public abstract void registerObserver(Observer observer);
-
- /**
- * Unregisters the previously registered observer.
- * Observer unregistration synchronized with signaling thread. If some data modified
- * in observer callbacks without additional synchronization it's safe to access
- * this data on the current thread after calling this method.
- */
- public abstract void unregisterObserver();
-
- /**
- * Sending message to the data channel.
- * Message size is |message|.remaining().
- */
- public abstract void send(ByteBuffer message, MessageType type);
-
- /**
- * Closing data channel. Both channels in the pair will change state to CLOSED.
- */
- public abstract void close();
-
- /**
- * Releases native objects (if any). Closes data channel. No other methods are allowed after it
- * (in multithread scenario needs synchronization with access to the data channel).
- */
- public abstract void dispose();
-}
diff --git a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelBase.java b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelBase.java
deleted file mode 100644
index 7c1439a5..0000000
--- a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelBase.java
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Base class for client and server that tunnels DevToolsServer's UNIX socket
- * over WebRTC data channel.
- *
- * Server runs on a android device with Chromium (or alike). Client runs where socket
- * needed to be accesses (it could be the same device if socket names are different; this
- * configuration useful for testing).
- *
- * Client listens LocalServerSocket and each time it receives connection it forwards
- * CLIENT_OPEN packet to the server with newly assigned connection id. On receiving this packet
- * server tries to connect to DevToolsServer socket. If succeeded it sends back SERVER_OPEN_ACK
- * with the same connection id. If failed it sends SERVER_CLOSE.
- *
- * When input stream on client shuts down it sends CLIENT_CLOSE. The same with SERVER_CLOSE
- * on the server side (only if SERVER_OPEN_ACK had sent). Between CLIENT_OPEN and CLIENT_CLOSE
- * any amount of data packets may be transferred (the same for SERVER_OPEN_ACK/SERVER_CLOSE
- * on the server side).
- *
- * Since all communication is reliable and ordered it's safe for client to assume that
- * if CLIENT_CLOSE has sent and SERVER_CLOSE has received with the same connection ID this
- * ID is safe to be reused.
- */
-public abstract class SocketTunnelBase {
- // Data channel is threadsafe but access to the reference needs synchromization.
- private final ReadWriteLock mDataChanneliReferenceLock = new ReentrantReadWriteLock();
- private volatile AbstractDataChannel mDataChannel;
-
- // Packet structure encapsulated in buildControlPacket, buildDataPacket and PacketDecoderBase.
- // Structure of control packet:
- // 1-st byte: CONTROL_CONNECTION_ID.
- // 2-d byte: op code.
- // 3-d byte: connection id.
- //
- // Structure of data packet:
- // 1-st byte: connection id.
- // 2..n: data.
-
- private static final int CONTROL_PACKET_SIZE = 3;
-
- // Client to server control packets.
- protected static final byte CLIENT_OPEN = (byte) 0;
- protected static final byte CLIENT_CLOSE = (byte) 1;
-
- // Server to client control packets.
- protected static final byte SERVER_OPEN_ACK = (byte) 0;
- protected static final byte SERVER_CLOSE = (byte) 1;
-
- // Must not exceed WebRTC limit. Exceeding it closes
- // data channel automatically. TODO(serya): WebRTC limit supposed to be removed.
- static final int READING_BUFFER_SIZE = 4 * 1024;
-
- private static final int CONTROL_CONNECTION_ID = 0;
-
- // DevTools supports up to ~10 connections at the time. A few extra IDs usefull for
- // delays in closing acknowledgement.
- protected static final int MIN_CONNECTION_ID = 1;
- protected static final int MAX_CONNECTION_ID = 64;
-
- // Signaling thread isn't accessible via API. Assumes that first caller
- // checkCalledOnSignalingThread is called on it indeed. It also works well for tests.
- private final AtomicReference<Thread> mSignalingThread = new AtomicReference<Thread>();
-
- // For writing in socket without blocking signaling thread.
- private final ExecutorService mWritingThread = Executors.newSingleThreadExecutor();
-
- public boolean isBound() {
- final Lock lock = mDataChanneliReferenceLock.readLock();
- lock.lock();
- try {
- return mDataChannel != null;
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Binds the tunnel to the data channel. Tunnel starts its activity when data channel
- * open.
- */
- public void bind(AbstractDataChannel dataChannel) {
- // Observer registrution must not be done in constructor.
- final Lock lock = mDataChanneliReferenceLock.writeLock();
- lock.lock();
- try {
- mDataChannel = dataChannel;
- } finally {
- lock.unlock();
- }
- dataChannel.registerObserver(new DataChannelObserver());
- }
-
- /**
- * Stops all tunnel activity and returns the prevously bound data channel.
- * It's safe to dispose the data channel after it.
- */
- public AbstractDataChannel unbind() {
- final Lock lock = mDataChanneliReferenceLock.writeLock();
- lock.lock();
- final AbstractDataChannel dataChannel;
- try {
- dataChannel = mDataChannel;
- mDataChannel = null;
- } finally {
- lock.unlock();
- }
- dataChannel.unregisterObserver();
- mSignalingThread.set(null);
- mWritingThread.shutdownNow();
- return dataChannel;
- }
-
- protected void checkCalledOnSignalingThread() {
- if (!mSignalingThread.compareAndSet(null, Thread.currentThread())) {
- if (mSignalingThread.get() != Thread.currentThread()) {
- throw new RuntimeException("Must be called on signaling thread");
- }
- }
- }
-
- protected static void checkConnectionId(int connectionId) throws ProtocolError {
- if (connectionId < MIN_CONNECTION_ID || connectionId > MAX_CONNECTION_ID) {
- throw new ProtocolError("Invalid connection id: " + Integer.toString(connectionId));
- }
- }
-
- protected void onProtocolError(ProtocolError e) {
- checkCalledOnSignalingThread();
-
- // When integrity of data channel is broken then best way to survive is to close it.
- final Lock lock = mDataChanneliReferenceLock.readLock();
- lock.lock();
- try {
- mDataChannel.close();
- } finally {
- lock.unlock();
- }
- }
-
- protected abstract void onReceivedDataPacket(int connectionId, byte[] data)
- throws ProtocolError;
- protected abstract void onReceivedControlPacket(int connectionId, byte opCode)
- throws ProtocolError;
- protected void onSocketException(IOException e, int connectionId) {}
- protected void onDataChannelOpened() {}
- protected void onDataChannelClosed() {}
-
- static ByteBuffer buildControlPacket(int connectionId, byte opCode) {
- ByteBuffer packet = ByteBuffer.allocateDirect(CONTROL_PACKET_SIZE);
- packet.put((byte) CONTROL_CONNECTION_ID);
- packet.put(opCode);
- packet.put((byte) connectionId);
- return packet;
- }
-
- static ByteBuffer buildDataPacket(int connectionId, byte[] buffer, int count) {
- ByteBuffer packet = ByteBuffer.allocateDirect(count + 1);
- packet.put((byte) connectionId);
- packet.put(buffer, 0, count);
- return packet;
- }
-
- protected void sendToDataChannel(ByteBuffer packet) {
- packet.limit(packet.position());
- packet.position(0);
- final Lock lock = mDataChanneliReferenceLock.readLock();
- lock.lock();
- try {
- if (mDataChannel != null) {
- mDataChannel.send(packet, AbstractDataChannel.MessageType.BINARY);
- }
- } finally {
- lock.unlock();
- }
- }
-
- /**
- * Packet decoding exposed for tests.
- */
- abstract static class PacketDecoderBase {
- protected void decodePacket(ByteBuffer packet) throws ProtocolError {
- if (packet.remaining() == 0) {
- throw new ProtocolError("Empty packet");
- }
-
- int connectionId = packet.get();
- if (connectionId != CONTROL_CONNECTION_ID) {
- checkConnectionId(connectionId);
- byte[] data = new byte[packet.remaining()];
- packet.get(data);
- onReceivedDataPacket(connectionId, data);
- } else {
- if (packet.remaining() != CONTROL_PACKET_SIZE - 1) {
- throw new ProtocolError("Invalid control packet size");
- }
-
- byte opCode = packet.get();
- connectionId = packet.get();
- checkConnectionId(connectionId);
- onReceivedControlPacket(connectionId, opCode);
- }
- }
-
- protected abstract void onReceivedDataPacket(int connectionId, byte[] data)
- throws ProtocolError;
- protected abstract void onReceivedControlPacket(int connectionId, byte opcode)
- throws ProtocolError;
- }
-
- private final class DataChannelObserver
- extends PacketDecoderBase implements AbstractDataChannel.Observer {
- @Override
- public void onStateChange(AbstractDataChannel.State state) {
- checkCalledOnSignalingThread();
-
- if (state == AbstractDataChannel.State.OPEN) {
- onDataChannelOpened();
- } else {
- onDataChannelClosed();
- }
- }
-
- @Override
- public void onMessage(ByteBuffer message) {
- checkCalledOnSignalingThread();
-
- try {
- decodePacket(message);
- } catch (ProtocolError e) {
- onProtocolError(e);
- }
- }
-
- @Override
- protected void onReceivedDataPacket(int connectionId, byte[] data) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- SocketTunnelBase.this.onReceivedDataPacket(connectionId, data);
- }
-
- @Override
- protected void onReceivedControlPacket(int connectionId, byte opCode) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- SocketTunnelBase.this.onReceivedControlPacket(connectionId, opCode);
- }
- }
-
- /**
- * Any problem happened while handling incoming message that breaks state integrity.
- */
- static class ProtocolError extends Exception {
- public ProtocolError(String description) {
- super(description);
- }
- }
-
- /**
- * Base utility class for client and server connections.
- */
- protected abstract class ConnectionBase {
- protected final int mId;
- protected final LocalSocket mSocket;
- private final AtomicInteger mOpenedStreams = new AtomicInteger(2); // input and output.
- private volatile boolean mConnected;
- private byte[] mBuffer;
-
- private ConnectionBase(int id, LocalSocket socket, boolean preconnected) {
- mId = id;
- mSocket = socket;
- mConnected = preconnected;
- }
-
- protected ConnectionBase(int id, LocalSocket socket) {
- this(id, socket, true);
- }
-
- protected ConnectionBase(int id) {
- this(id, new LocalSocket(), false);
- }
-
- protected boolean connect(LocalSocketAddress address) {
- assert !mConnected;
- try {
- mSocket.connect(address);
- mConnected = true;
- return true;
- } catch (IOException e) {
- onSocketException(e, mId);
- return false;
- }
- }
-
- protected void runReadingLoop() {
- mBuffer = new byte[READING_BUFFER_SIZE];
- try {
- boolean open;
- do {
- open = pump();
- } while (open);
- } catch (IOException e) {
- onSocketException(e, mId);
- } finally {
- mBuffer = null;
- }
- }
-
- private boolean pump() throws IOException {
- int count = mSocket.getInputStream().read(mBuffer);
- if (count <= 0)
- return false;
- sendToDataChannel(buildDataPacket(mId, mBuffer, count));
- return true;
- }
-
- protected void writeData(byte[] data) {
- // Called on writing thread.
- try {
- mSocket.getOutputStream().write(data);
- } catch (IOException e) {
- onSocketException(e, mId);
- }
- }
-
- public void onReceivedDataPacket(final byte[] data) {
- mWritingThread.execute(new Runnable() {
- @Override
- public void run() {
- writeData(data);
- }
- });
- }
-
- public void terminate() {
- close();
- }
-
- protected void shutdownOutput() {
- // Shutdown output on writing thread to make sure all pending writes finished.
- mWritingThread.execute(new Runnable() {
- @Override
- public void run() {
- shutdownOutputOnWritingThread();
- }
- });
- }
-
- private void shutdownOutputOnWritingThread() {
- try {
- if (mConnected) mSocket.shutdownOutput();
- } catch (IOException e) {
- onSocketException(e, mId);
- }
- releaseStream();
- }
-
- protected void shutdownInput() {
- try {
- if (mConnected) mSocket.shutdownInput();
- } catch (IOException e) {
- onSocketException(e, mId);
- }
- releaseStream();
- }
-
- private void releaseStream() {
- if (mOpenedStreams.decrementAndGet() == 0) close();
- }
-
- protected void close() {
- try {
- mSocket.close();
- } catch (IOException e) {
- onSocketException(e, mId);
- }
- }
- }
-}
diff --git a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelServer.java b/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelServer.java
deleted file mode 100644
index 7ef0c95..0000000
--- a/components/devtools_bridge/android/java/src/org/chromium/components/devtools_bridge/SocketTunnelServer.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge;
-
-import android.net.LocalSocketAddress;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Tunnels DevTools UNIX socket over AbstractDataChannel.
- */
-public class SocketTunnelServer extends SocketTunnelBase {
- private final LocalSocketAddress mAddress;
-
- private final ExecutorService mReadingThreadPool = Executors.newCachedThreadPool();
-
- // Connections with opened client to server stream. If bound to data channel must be accessed
- // on signaling thread.
- private final Map<Integer, Connection> mClientConnections =
- new HashMap<Integer, Connection>();
-
- // Connections with opened server to client stream. Values are added
- // on signaling thread and removed on reading thread.
- private final ConcurrentMap<Integer, Connection> mServerConnections =
- new ConcurrentHashMap<Integer, Connection>();
-
- public SocketTunnelServer(String socketName) {
- mAddress = new LocalSocketAddress(socketName);
- }
-
- @Override
- public AbstractDataChannel unbind() {
- AbstractDataChannel dataChannel = super.unbind();
-
- mReadingThreadPool.shutdownNow();
-
- // Safe to access mClientConnections here once AbstractDataChannel.Observer detached.
- for (Connection connection : mClientConnections.values()) {
- connection.terminate();
- }
- mClientConnections.clear();
- return dataChannel;
- }
-
- public boolean hasConnections() {
- return mClientConnections.size() + mServerConnections.size() > 0;
- }
-
- @Override
- protected void onReceivedDataPacket(int connectionId, byte[] data) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- if (!mClientConnections.containsKey(connectionId)) {
- throw new ProtocolError("Unknows conection id");
- }
-
- mClientConnections.get(connectionId).onReceivedDataPacket(data);
- }
-
- @Override
- protected void onReceivedControlPacket(int connectionId, byte opCode) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- switch (opCode) {
- case CLIENT_OPEN:
- onClientOpen(connectionId);
- break;
-
- case CLIENT_CLOSE:
- onClientClose(connectionId);
- break;
-
- default:
- throw new ProtocolError("Invalid opCode");
- }
- }
-
- private void onClientOpen(int connectionId) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- if (mClientConnections.containsKey(connectionId) ||
- mServerConnections.containsKey(connectionId)) {
- throw new ProtocolError("Conection id already used");
- }
-
- Connection connection = new Connection(connectionId);
- mClientConnections.put(connectionId, connection);
- mServerConnections.put(connectionId, connection);
-
- mReadingThreadPool.execute(connection);
- }
-
- private void onClientClose(int connectionId) throws ProtocolError {
- checkCalledOnSignalingThread();
-
- if (!mClientConnections.containsKey(connectionId)) {
- throw new ProtocolError("Unknows connection id");
- }
-
- Connection connection = mClientConnections.get(connectionId);
-
- connection.closedByClient();
- mClientConnections.remove(connectionId);
- }
-
- private final class Connection extends ConnectionBase implements Runnable {
- public Connection(int id) {
- super(id);
- }
-
- public void closedByClient() {
- shutdownOutput();
- }
-
- @Override
- public void run() {
- assert mServerConnections.containsKey(mId);
-
- if (connect(mAddress)) {
- sendToDataChannel(buildControlPacket(mId, SERVER_OPEN_ACK));
- runReadingLoop();
- }
- mServerConnections.remove(mId);
- shutdownInput();
- sendToDataChannel(buildControlPacket(mId, SERVER_CLOSE));
- }
- }
-}
diff --git a/components/devtools_bridge/android/javatests/AndroidManifest.xml b/components/devtools_bridge/android/javatests/AndroidManifest.xml
deleted file mode 100644
index 2478af1..0000000
--- a/components/devtools_bridge/android/javatests/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
- <!-- Copyright 2014 The Chromium Authors. All rights reserved. Use of
- this source code is governed by a BSD-style license that can be found
- in the LICENSE file. -->
- <!-- package name must be unique so suffix with "tests" so package loader
- doesn't ignore this. -->
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.chromium.components.devtools_bridge.tests">
- <application>
- <uses-library android:name="android.test.runner" />
- <service
- android:name=".DebugService" >
- </service>
- <activity
- android:name=".DebugActivity"
- android:label="DevToolsBridge tests" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="20" />
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="org.chromium.components.devtools_bridge.tests"
- android:label="Tests for org.chromium.components.devtools_bridge"/>
-
- <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
- <uses-permission android:name="android.permission.INJECT_EVENTS" />
-
- <!-- For manual testing with Chrome Shell -->
- <uses-permission android:name="org.chromium.chrome.shell.permission.DEBUG" />
-
- <!-- For manual testing with Clankium -->
- <uses-permission android:name="com.google.android.apps.chrome.permission.DEBUG" />
-</manifest>
diff --git a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridgeTest.java b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridgeTest.java
deleted file mode 100644
index 6810c28..0000000
--- a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridgeTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge;
-
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.Assert;
-
-import java.io.IOException;
-import java.util.concurrent.Future;
-
-/**
- * Tests for {@link SocketTunnelBridge}
- */
-public class LocalTunnelBridgeTest extends InstrumentationTestCase {
- private static final String REQUEST = "Request";
- private static final String RESPONSE = "Response";
-
- private static final String SERVER_SOCKET_NAME =
- "org.chromium.components.devtools_bridge.LocalTunnelBridgeTest.SERVER_SOCKET";
- private static final String CLIENT_SOCKET_NAME =
- "org.chromium.components.devtools_bridge.LocalTunnelBridgeTest.CLIENT_SOCKET";
-
- private LocalTunnelBridge mBridge;
- private LocalServerSocket mServerSocket;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mServerSocket = new LocalServerSocket(SERVER_SOCKET_NAME);
- }
-
- private void startBridge() throws IOException {
- startBridge(SERVER_SOCKET_NAME);
- }
-
- private void startBridge(String serverSocketName) throws IOException {
- Assert.assertNull(mBridge);
- mBridge = new LocalTunnelBridge(serverSocketName, CLIENT_SOCKET_NAME);
- mBridge.start();
- }
-
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- if (mBridge != null) {
- mBridge.dispose();
- mBridge = null;
- }
- mServerSocket.close();
- }
-
- @SmallTest
- public void testStartStop() throws Exception {
- startBridge();
- mBridge.stop();
- }
-
- @SmallTest
- public void testRequestResponse() throws Exception {
- startBridge();
-
- Future<String> response = TestUtils.asyncRequest(CLIENT_SOCKET_NAME, REQUEST);
-
- LocalSocket socket = mServerSocket.accept();
- String request = TestUtils.readAll(socket);
- TestUtils.writeAndShutdown(socket, RESPONSE);
-
- Assert.assertEquals(REQUEST, request);
-
- Assert.assertEquals(RESPONSE, response.get());
- socket.close();
-
- mBridge.stop();
- }
-
- @SmallTest
- public void testRequestFailure1() throws Exception {
- startBridge();
-
- Future<String> response = TestUtils.asyncRequest(CLIENT_SOCKET_NAME, REQUEST);
- LocalSocket socket = mServerSocket.accept();
- int firstByte = socket.getInputStream().read();
-
- Assert.assertEquals((int) REQUEST.charAt(0), firstByte);
-
- socket.close();
-
- Assert.assertEquals("", response.get());
-
- mBridge.waitAllConnectionsClosed();
- mBridge.stop();
- }
-
- @SmallTest
- public void testRequestFailure2() throws Exception {
- startBridge("jdwp-control"); // Android system socket will reject connection.
-
- Future<String> response = TestUtils.asyncRequest(CLIENT_SOCKET_NAME, REQUEST);
-
- Assert.assertEquals("", response.get());
-
- mBridge.waitAllConnectionsClosed();
- mBridge.stop();
- }
-}
diff --git a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java
deleted file mode 100644
index e6e58eb..0000000
--- a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge;
-
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import junit.framework.Assert;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-/**
- * Tests for {@link SocketTunnelServer}
- */
-public class SocketTunnelServerTest extends InstrumentationTestCase {
- private static final int CONNECTION_ID = 30;
- private static final String SOCKET_NAME = "ksdjhflksjhdflk";
-
- private DataChannelMock mDataChannelMock;
- private SocketTunnelServer mServer;
- private LocalServerSocket mSocket;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mSocket = new LocalServerSocket(SOCKET_NAME);
- }
-
- @Override
- public void tearDown() throws Exception {
- mSocket.close();
- if (mServer != null) destroyServer();
- super.tearDown();
- }
-
- private void createServer() {
- createServer(new DataChannelMock());
- }
-
- private void createServer(DataChannelMock dataChannel) {
- mDataChannelMock = dataChannel;
- mServer = new SocketTunnelServer(SOCKET_NAME);
- mServer.bind(mDataChannelMock);
- }
-
- private void destroyServer() {
- mServer.unbind().dispose();
- mServer = null;
- }
-
- @SmallTest
- public void testOpenDataChannel() {
- createServer();
- mDataChannelMock.open();
- }
-
- @SmallTest
- public void testDecodeControlPacket() {
- createServer();
- ByteBuffer packet = buildControlPacket(CONNECTION_ID, SocketTunnelBase.SERVER_OPEN_ACK);
-
- PacketDecoder decoder = PacketDecoder.decode(packet);
- Assert.assertTrue(decoder.isControlPacket());
- Assert.assertEquals(CONNECTION_ID, decoder.connectionId());
- Assert.assertEquals(SocketTunnelBase.SERVER_OPEN_ACK, decoder.opCode());
- }
-
- @SmallTest
- public void testConnectToSocket() throws IOException {
- createServer();
- LocalSocket socket = connectToSocket(1);
- Assert.assertTrue(mServer.hasConnections());
- socket.close();
- }
-
- private LocalSocket connectToSocket(int connectionId) throws IOException {
- mDataChannelMock.notifyMessage(
- buildControlPacket(connectionId, SocketTunnelBase.CLIENT_OPEN));
- return mSocket.accept();
- }
-
- private void sendClose(int connectionId) {
- mDataChannelMock.notifyMessage(
- buildControlPacket(connectionId, SocketTunnelBase.CLIENT_CLOSE));
- }
-
- private ByteBuffer buildControlPacket(int connectionId, byte opCode) {
- ByteBuffer packet = SocketTunnelBase.buildControlPacket(connectionId, opCode);
- packet.limit(packet.position());
- packet.position(0);
- Assert.assertTrue(packet.remaining() > 0);
- return packet;
- }
-
- private ByteBuffer buildDataPacket(int connectionId, byte[] data) {
- ByteBuffer packet = SocketTunnelBase.buildDataPacket(connectionId, data, data.length);
- packet.limit(packet.position());
- packet.position(0);
- Assert.assertTrue(packet.remaining() > 0);
- return packet;
- }
-
- @SmallTest
- public void testReceiveOpenAcknowledgement() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
-
- receiveOpenAck(CONNECTION_ID);
-
- socket.close();
- }
-
- private PacketDecoder receiveControlPacket(int connectionId) throws InterruptedException {
- PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive());
- Assert.assertTrue(decoder.isControlPacket());
- Assert.assertEquals(connectionId, decoder.connectionId());
- return decoder;
- }
-
- private void receiveOpenAck(int connectionId) throws InterruptedException {
- PacketDecoder decoder = receiveControlPacket(connectionId);
- Assert.assertEquals(SocketTunnelBase.SERVER_OPEN_ACK, decoder.opCode());
- }
-
- private void receiveClose(int connectionId) throws InterruptedException {
- PacketDecoder decoder = receiveControlPacket(connectionId);
- Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode());
- }
-
- @SmallTest
- public void testClosingSocket() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
-
- socket.shutdownOutput();
-
- PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive());
-
- Assert.assertTrue(decoder.isControlPacket());
- Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode());
- Assert.assertEquals(CONNECTION_ID, decoder.connectionId());
-
- socket.close();
- }
-
- @SmallTest
- public void testReadData() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
-
- byte[] sample = "Sample".getBytes();
-
- socket.getOutputStream().write(sample);
- socket.getOutputStream().flush();
- socket.shutdownOutput();
-
- ByteBuffer result = receiveData(CONNECTION_ID, sample.length);
- Assert.assertEquals(ByteBuffer.wrap(sample), result);
- }
-
- private ByteBuffer receiveData(int connectionId, int length) throws InterruptedException {
- ByteBuffer result = ByteBuffer.allocate(length);
-
- while (true) {
- PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive());
- if (decoder.isDataPacket()) {
- Assert.assertEquals(connectionId, decoder.connectionId());
- result.put(decoder.data());
- } else if (decoder.isControlPacket()) {
- Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode());
- Assert.assertEquals(connectionId, decoder.connectionId());
- break;
- }
- }
- result.limit(result.position());
- result.position(0);
- return result;
- }
-
- private int sum(int[] values) {
- int result = 0;
- for (int v : values)
- result += v;
- return result;
- }
-
- private static final int[] CHUNK_SIZES =
- new int[] { 0, 1, 5, 100, 1000, SocketTunnelBase.READING_BUFFER_SIZE * 2 };
-
- @SmallTest
- public void testReadLongDataChunk() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
-
- byte[] buffer = new byte[CHUNK_SIZES[CHUNK_SIZES.length - 1]];
- ByteBuffer sentData = ByteBuffer.allocate(sum(CHUNK_SIZES));
- OutputStream stream = socket.getOutputStream();
- byte next = 0;
- int prevSize = 0;
- for (int size : CHUNK_SIZES) {
- while (prevSize < size)
- buffer[prevSize++] = next++;
-
- stream.write(buffer, 0, size);
- sentData.put(buffer, 0, size);
- }
-
- socket.shutdownOutput();
-
- sentData.limit(sentData.position());
- sentData.position(0);
- ByteBuffer readData = receiveData(CONNECTION_ID, sentData.limit());
-
- Assert.assertEquals(sentData, readData);
- }
-
- @SmallTest
- public void testReuseConnectionId() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
-
- socket.shutdownOutput();
- socket.close();
- receiveClose(CONNECTION_ID);
- sendClose(CONNECTION_ID);
-
- // Open connection with the same ID
- socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
- }
-
- private static final byte[] SAMPLE = "Sample".getBytes();
-
- @SmallTest
- public void testWriteData() throws IOException, InterruptedException {
- createServer();
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- receiveOpenAck(CONNECTION_ID);
-
- mDataChannelMock.notifyMessage(buildDataPacket(CONNECTION_ID, SAMPLE));
-
- byte[] result = new byte[SAMPLE.length];
- int read = 0;
- while (read < SAMPLE.length) {
- int count = socket.getInputStream().read(result, 0, SAMPLE.length - read);
- Assert.assertTrue(count > 0);
- read += count;
- }
-
- Assert.assertEquals(ByteBuffer.wrap(SAMPLE), ByteBuffer.wrap(result));
-
- socket.close();
- }
-
- private enum TestStates {
- INITIAL, SENDING, CLOSING, MAY_FINISH_SENDING, SENT, DONE
- }
-
- @MediumTest
- public void testStopWhileSendingData() throws IOException {
-
- final TestUtils.StateBarrier<TestStates> barrier =
- new TestUtils.StateBarrier<TestStates>(TestStates.INITIAL);
-
- createServer(new DataChannelMock() {
- @Override
- public void send(ByteBuffer message, AbstractDataChannel.MessageType type) {
- barrier.advance(TestStates.INITIAL, TestStates.SENDING);
- barrier.advance(TestStates.MAY_FINISH_SENDING, TestStates.SENT);
- }
- });
-
- LocalSocket socket = connectToSocket(CONNECTION_ID);
- barrier.advance(TestStates.SENDING, TestStates.CLOSING);
- socket.close();
-
- new Thread() {
- @Override
- public void run() {
- sleep();
- barrier.advance(TestStates.CLOSING, TestStates.MAY_FINISH_SENDING);
- }
- }.start();
-
- destroyServer();
-
- barrier.advance(TestStates.SENT, TestStates.DONE);
- }
-
- private void sleep() {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugActivity.java b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugActivity.java
deleted file mode 100644
index 4e769ec..0000000
--- a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugActivity.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge.tests;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-import android.widget.TextView;
-
-/**
- * Activity for testing devtools bridge.
- */
-public class DebugActivity extends Activity {
- private LinearLayout mLayout;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mLayout = new LinearLayout(this);
- mLayout.setOrientation(LinearLayout.VERTICAL);
-
- String intro = "To test LocalTunnelBridge manually: \n" +
- "1. Enable USB debugging.\n" +
- "2. Run ChromeShell along with this app.\n" +
- "3. Start the LocalTunnelBridge.\n" +
- "4. Connect the device to a desktop via USB.\n" +
- "5. Open chrome://inspect#devices on desktop Chrome.\n" +
- "6. Observe 2 identical Chrome Shells on the device.";
-
- TextView textView = new TextView(this);
- textView.setText(intro);
- mLayout.addView(textView);
-
- Button startButton = new Button(this);
- startButton.setText("Start LocalTunnelBridge");
- startButton.setOnClickListener(new SendActionOnClickListener(DebugService.START_ACTION));
- mLayout.addView(startButton);
-
- Button stopButton = new Button(this);
- stopButton.setText("Stop");
- stopButton.setOnClickListener(new SendActionOnClickListener(DebugService.STOP_ACTION));
- mLayout.addView(stopButton);
-
- LayoutParams layoutParam = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
- setContentView(mLayout, layoutParam);
- }
-
- private class SendActionOnClickListener implements View.OnClickListener {
- private final String mAction;
-
- SendActionOnClickListener(String action) {
- mAction = action;
- }
-
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(DebugActivity.this, DebugService.class);
- intent.setAction(mAction);
- startService(intent);
- }
- }
-}
-
diff --git a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugService.java b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugService.java
deleted file mode 100644
index 8d23016..0000000
--- a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/tests/DebugService.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.devtools_bridge.tests;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.widget.Toast;
-
-import org.chromium.components.devtools_bridge.LocalTunnelBridge;
-
-import java.io.IOException;
-
-/**
- * Service for testing devtools bridge.
- */
-public class DebugService extends Service {
- private static final String PACKAGE = "org.chromium.components.devtools_bridge.tests";
- public static final String START_ACTION = PACKAGE + ".START_ACTION";
- public static final String STOP_ACTION = PACKAGE + ".STOP_ACTION";
- private static final int NOTIFICATION_ID = 1;
-
- private LocalTunnelBridge mBridge;
-
- private LocalTunnelBridge createBridge() throws IOException {
- String exposingSocketName = "webview_devtools_remote_" + Integer.valueOf(Process.myPid());
- return new LocalTunnelBridge("chrome_shell_devtools_remote", exposingSocketName);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent == null) return START_NOT_STICKY;
-
- String action = intent.getAction();
- if (START_ACTION.equals(action)) {
- return start();
- } else if (STOP_ACTION.equals(action)) {
- return stop();
- }
- return START_NOT_STICKY;
- }
-
- private int start() {
- if (mBridge != null)
- return START_NOT_STICKY;
-
- try {
- mBridge = createBridge();
- mBridge.start();
- } catch (Exception e) {
- Toast.makeText(this, "Failed to start", Toast.LENGTH_SHORT).show();
- mBridge.dispose();
- mBridge = null;
- return START_NOT_STICKY;
- }
-
- startForeground(NOTIFICATION_ID, makeForegroundServiceNotification());
- Toast.makeText(this, "Service started", Toast.LENGTH_SHORT).show();
- return START_STICKY;
- }
-
- private int stop() {
- if (mBridge == null)
- return START_NOT_STICKY;
-
- mBridge.stop();
- mBridge.dispose();
- mBridge = null;
- stopSelf();
- Toast.makeText(this, "Service stopped", Toast.LENGTH_SHORT).show();
- return START_NOT_STICKY;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return new Binder();
- }
-
- private Notification makeForegroundServiceNotification() {
- Intent showInfoIntent = new Intent(this, DebugActivity.class);
- showInfoIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- PendingIntent showInfoPendingIntent =
- PendingIntent.getActivity(DebugService.this, 0, showInfoIntent, 0);
-
- Intent stopIntent = new Intent(this, DebugService.class);
- stopIntent.setAction(STOP_ACTION);
- PendingIntent stopPendingIntent =
- PendingIntent.getService(DebugService.this, 0, stopIntent, 0);
-
- return new Notification.Builder(this)
- // Mandatory fiends
- .setSmallIcon(android.R.drawable.alert_dark_frame)
- .setContentTitle("DevTools Bridge")
- .setContentText("DevTools socket local test tunnel works")
-
- // Optional
- .setContentIntent(showInfoPendingIntent)
- .addAction(android.R.drawable.ic_delete,
- "Stop", stopPendingIntent)
- .setOngoing(true)
- .setWhen(System.currentTimeMillis())
- .build();
- }
-}
-