diff options
author | phoglund <phoglund@chromium.org> | 2014-09-24 00:32:58 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-24 07:33:14 +0000 |
commit | cf0900dae461d90af8ed7604da098690d7c744c3 (patch) | |
tree | 49f818e6b5144cc417c5fc989118879e34786c37 | |
parent | 15044609893faf9b2c2a533aa9a136f288ea20a1 (diff) | |
download | chromium_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}
23 files changed, 1 insertions, 2373 deletions
diff --git a/build/all.gyp b/build/all.gyp index 4cafea2..7f6d6c4 100644 --- a/build/all.gyp +++ b/build/all.gyp @@ -800,7 +800,6 @@ '../chrome/chrome.gyp:chrome_shell_uiautomator_tests', '../chrome/chrome.gyp:unit_tests_apk', '../components/components_tests.gyp:components_unittests_apk', - '../components/devtools_bridge.gyp:devtools_bridge_tests_apk', '../content/content_shell_and_tests.gyp:content_browsertests_apk', '../content/content_shell_and_tests.gyp:content_gl_tests_apk', '../content/content_shell_and_tests.gyp:content_unittests_apk', diff --git a/build/android/pylib/gtest/gtest_config.py b/build/android/pylib/gtest/gtest_config.py index aeca27e..65d7fc0 100644 --- a/build/android/pylib/gtest/gtest_config.py +++ b/build/android/pylib/gtest/gtest_config.py @@ -8,7 +8,6 @@ EXPERIMENTAL_TEST_SUITES = [ 'content_gl_tests', 'heap_profiler_unittests', - 'devtools_bridge_tests', ] # Do not modify this list without approval of an android owner. diff --git a/chrome/android/shell/java/AndroidManifest.xml b/chrome/android/shell/java/AndroidManifest.xml index 442b5b5..84ca1957 100644 --- a/chrome/android/shell/java/AndroidManifest.xml +++ b/chrome/android/shell/java/AndroidManifest.xml @@ -32,11 +32,6 @@ <uses-permission android:name="org.chromium.chrome.shell.permission.C2D_MESSAGE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> - <permission android:name="org.chromium.chrome.shell.permission.DEBUG" - android:label="Debug web pages in Chrome Shell" - android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS" - android:protectionLevel="signature" /> - <application android:name="org.chromium.chrome.shell.ChromeShellApplication" android:icon="@mipmap/app_icon" android:label="Chrome Shell"> diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java index 62bbbad..5f34230 100644 --- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java +++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java @@ -167,8 +167,7 @@ public class ChromeShellActivity extends Activity implements AppMenuPropertiesDe mToolbar.setMenuHandler(mAppMenuHandler); mDevToolsServer = new DevToolsServer("chrome_shell"); - mDevToolsServer.setRemoteDebuggingEnabled( - true, DevToolsServer.Security.ALLOW_DEBUG_PERMISSION); + mDevToolsServer.setRemoteDebuggingEnabled(true); mPrintingController = PrintingControllerFactory.create(this); diff --git a/components/OWNERS b/components/OWNERS index ee83a1f..afc7389 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -31,9 +31,6 @@ per-file data_reduction_proxy*=bengr@chromium.org per-file data_reduction_proxy*=marq@chromium.org per-file data_reduction_proxy*=bolian@chromium.org -per-file devtools_bridge.gyp=mnaganov@chromium.org -per-file devtools_bridge.gyp=serya@chromium.org - per-file dom_distiller*=bengr@chromium.org per-file dom_distiller*=cjhopman@chromium.org per-file dom_distiller*=nyquist@chromium.org diff --git a/components/devtools_bridge.gyp b/components/devtools_bridge.gyp deleted file mode 100644 index 9df9527..0000000 --- a/components/devtools_bridge.gyp +++ /dev/null @@ -1,42 +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. - -{ - 'targets': [ - { - 'target_name': 'devtools_bridge_javalib', - 'type': 'none', - 'variables': { - 'java_in_dir': 'devtools_bridge/android/java', - }, - 'includes': [ '../build/java.gypi' ], - }, - { - 'target_name': 'devtools_bridge_testutils', - 'type': 'none', - 'variables': { - 'java_in_dir': 'devtools_bridge/test/android/javatests', - }, - 'includes': [ '../build/java.gypi' ], - 'dependencies': [ - 'devtools_bridge_javalib', - ], - }, - { - 'target_name': 'devtools_bridge_tests_apk', - 'type': 'none', - 'dependencies': [ - 'devtools_bridge_javalib', - 'devtools_bridge_testutils', - ], - 'variables': { - 'apk_name': 'DevToolsBridgeTest', - 'test_suite_name': 'devtools_bridge_tests', - 'java_in_dir': 'devtools_bridge/android/javatests', - 'is_test_apk': 1, - }, - 'includes': [ '../build/java_apk.gypi' ], - }, - ], -} diff --git a/components/devtools_bridge/OWNERS b/components/devtools_bridge/OWNERS deleted file mode 100644 index 1b209e2..0000000 --- a/components/devtools_bridge/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -mnaganov@chromium.org -serya@chromium.org diff --git a/components/devtools_bridge/README b/components/devtools_bridge/README deleted file mode 100644 index 40077c9..0000000 --- a/components/devtools_bridge/README +++ /dev/null @@ -1,2 +0,0 @@ -The devtools_bridge component contains code for tunneling DevTools connection -over P2P data channel for remote debugging android devices. 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(); - } -} - diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataChannelMock.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataChannelMock.java deleted file mode 100644 index 9821de1..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataChannelMock.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 junit.framework.Assert; - -import java.nio.ByteBuffer; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * Mock of AbstractDataChannel tests. Also base class for DataPipe channels. - */ -public class DataChannelMock extends AbstractDataChannel { - private final SignalingThreadMock mSignalingThread; - private Observer mObserver; - private boolean mOpen = false; - private final LinkedBlockingQueue<ByteBuffer> mQueue = new LinkedBlockingQueue<ByteBuffer>(); - - // |signalingThread| will be disposed in the |dispose| method. If successor needs - // to control it's lifetime it must override |disposeSignalingThread| and not to invoke super's - // implementation. - protected DataChannelMock(SignalingThreadMock signalingThread) { - mSignalingThread = signalingThread; - } - - public DataChannelMock() { - this(new SignalingThreadMock()); - } - - public void open() { - onStateChange(AbstractDataChannel.State.OPEN); - } - - @Override - public void close() { - onStateChange(AbstractDataChannel.State.CLOSED); - } - - private void onStateChange(final State state) { - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - mObserver.onStateChange(state); - } - }); - } - - // Sends onMessage to the observer. - public void notifyMessage(ByteBuffer data) { - final byte[] bytes = toByteArray(data); - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - notifyMessageOnSignalingThread(ByteBuffer.wrap(bytes)); - } - }); - } - - protected void notifyMessageOnSignalingThread(ByteBuffer buffer) { - mObserver.onMessage(buffer); - } - - // Blocks until message received. Removes it from the queue and returns. - public ByteBuffer receive() { - try { - return mQueue.take(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - @Override - public void registerObserver(final Observer observer) { - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - Assert.assertNull(mObserver); - mObserver = observer; - Assert.assertNotNull(mObserver); - } - }); - } - - @Override - public void unregisterObserver() { - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - Assert.assertNotNull(mObserver); - mObserver = null; - } - }); - } - - private byte[] toByteArray(ByteBuffer data) { - final byte[] result = new byte[data.remaining()]; - data.get(result); - return result; - } - - @Override - public void send(ByteBuffer message, MessageType type) { - final byte[] data = toByteArray(message); - assert data.length > 0; - mSignalingThread.post(new Runnable() { - @Override - public void run() { - sendOnSignalingThread(ByteBuffer.wrap(data)); - android.util.Log.d("DataChannelMock", "Packet sent."); - } - }); - } - - protected void sendOnSignalingThread(ByteBuffer message) { - boolean success = mQueue.offer(message); - assert success; - } - - @Override - public void dispose() { - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - Assert.assertNull(mObserver); - } - }); - disposeSignalingThread(); - } - - protected void disposeSignalingThread() { - mSignalingThread.dispose(); - } -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataPipe.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataPipe.java deleted file mode 100644 index c36319a..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/DataPipe.java +++ /dev/null @@ -1,79 +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; - -/** - * Represents a pair of connected AbstractDataChannel's. Sends to one channel - * come to another and vice versa. - */ -public class DataPipe { - private final SignalingThreadMock mSignalingThread = new SignalingThreadMock(); - - private final PairedDataChannel mDC0 = new PairedDataChannel(); - private final PairedDataChannel mDC1 = new PairedDataChannel(); - - public void connect() { - mDC0.setPair(mDC1); - mDC1.setPair(mDC0); - mDC0.open(); - mDC1.open(); - } - - public void disconnect() { - mDC0.setPair(null); - mDC1.setPair(null); - mDC0.close(); - mDC1.close(); - } - - public AbstractDataChannel dataChannel(int index) { - switch (index) { - case 0: - return mDC0; - case 1: - return mDC1; - default: - throw new IllegalArgumentException("index"); - } - } - - public void dispose() { - mDC0.dispose(); - mDC1.dispose(); - mSignalingThread.dispose(); - } - - private class PairedDataChannel extends DataChannelMock { - private PairedDataChannel mPair; - - public PairedDataChannel() { - super(mSignalingThread); - } - - public void setPair(final PairedDataChannel pair) { - mSignalingThread.invoke(new Runnable() { - @Override - public void run() { - mPair = pair; - } - }); - } - - @Override - protected void sendOnSignalingThread(ByteBuffer message) { - assert message.remaining() > 0; - - if (mPair == null) return; - mPair.notifyMessageOnSignalingThread(message); - } - - @Override - protected void disposeSignalingThread() { - // Ignore. Will dispose in DataPipe.dispose. - } - } -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridge.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridge.java deleted file mode 100644 index c42909f..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalTunnelBridge.java +++ /dev/null @@ -1,190 +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.util.Log; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.concurrent.CountDownLatch; - -/** - * It allows testing DevTools socket tunneling on a single device. - * - * SocketTunnelClient opens LocalServerSocket named |socketToExpose| and - * tunnels all incoming connections to |socketToReplicate| using - * SocketTunnelServer and DataPipe between them. All data passes through - * WebRTC data channel but doens't leave the device. - */ -public class LocalTunnelBridge { - private static final String TAG = "LocalTunnelBridge"; - - private final DataPipe mPipe; - private final SocketTunnelServer mServer; - private final SocketTunnelClient mClient; - private boolean mLogPackets = false; - - private final CountDownLatch mServerDataChannelOpenedFlag = new CountDownLatch(1); - private final CountDownLatch mServerDataChannelClosedFlag = new CountDownLatch(1); - - public LocalTunnelBridge(String socketToReplicate, String socketToExpose) throws IOException { - mPipe = new DataPipe(); - - mServer = new SocketTunnelServer(socketToReplicate) { - @Override - protected void onProtocolError(ProtocolError e) { - throw new RuntimeException("Protocol error on server", e); - } - - @Override - protected void sendToDataChannel(ByteBuffer packet) { - if (mLogPackets) - Log.d(TAG, "Sending " + stringifyServerPacket(packet)); - super.sendToDataChannel(packet); - } - - @Override - protected void onReceivedDataPacket(int connectionId, byte[] data) - throws ProtocolError { - if (mLogPackets) { - Log.d(TAG, "Received client data packet with " + - Integer.toString(data.length) + " bytes"); - } - super.onReceivedDataPacket(connectionId, data); - } - - @Override - protected void onReceivedControlPacket(int connectionId, byte opCode) - throws ProtocolError { - if (mLogPackets) { - Log.d(TAG, "Received client control packet"); - } - super.onReceivedControlPacket(connectionId, opCode); - } - - @Override - protected void onSocketException(IOException e, int connectionId) { - Log.d(TAG, "Server socket exception on " + e + - " (connection " + Integer.toString(connectionId) + ")"); - super.onSocketException(e, connectionId); - } - - protected void onDataChannelOpened() { - Log.d(TAG, "Server data channel opened"); - super.onDataChannelOpened(); - mServerDataChannelOpenedFlag.countDown(); - } - - protected void onDataChannelClosed() { - Log.d(TAG, "Client data channel opened"); - super.onDataChannelClosed(); - mServerDataChannelClosedFlag.countDown(); - } - }; - - mServer.bind(mPipe.dataChannel(0)); - - mClient = new SocketTunnelClient(socketToExpose) { - @Override - protected void onProtocolError(ProtocolError e) { - throw new RuntimeException("Protocol error on client" + e); - } - - @Override - protected void onReceivedDataPacket(int connectionId, byte[] data) - throws ProtocolError { - if (mLogPackets) { - Log.d(TAG, "Received server data packet with " - + Integer.toString(data.length) + " bytes"); - } - super.onReceivedDataPacket(connectionId, data); - } - - @Override - protected void onReceivedControlPacket(int connectionId, byte opCode) - throws ProtocolError { - if (mLogPackets) { - Log.d(TAG, "Received server control packet"); - } - super.onReceivedControlPacket(connectionId, opCode); - } - - @Override - protected void sendToDataChannel(ByteBuffer packet) { - if (mLogPackets) { - Log.d(TAG, "Sending " + stringifyClientPacket(packet)); - } - super.sendToDataChannel(packet); - } - }; - mClient.bind(mPipe.dataChannel(1)); - } - - public void start() { - mPipe.connect(); - } - - public void stop() { - mPipe.disconnect(); - } - - public void dispose() { - mClient.unbind(); - mServer.unbind(); - mPipe.dispose(); - } - - public void waitAllConnectionsClosed() throws InterruptedException { - while (mServer.hasConnections() || mClient.hasConnections()) { - Thread.sleep(50); - } - } - - private String stringifyDataPacket(String type, PacketDecoder decoder) { - if (!decoder.isDataPacket()) { - throw new RuntimeException("Invalid packet"); - } - return type + "_DATA:" + Integer.toString(decoder.data().length); - } - - private String stringifyClientPacket(ByteBuffer packet) { - PacketDecoder decoder = decode(packet); - if (!decoder.isControlPacket()) - return stringifyDataPacket("CLIENT", decoder); - switch (decoder.opCode()) { - case SocketTunnelBase.CLIENT_OPEN: - return "CLIENT_OPEN " + Integer.valueOf(decoder.connectionId()); - case SocketTunnelBase.CLIENT_CLOSE: - return "CLIENT_CLOSE " + Integer.valueOf(decoder.connectionId()); - default: - throw new RuntimeException("Invalid packet"); - } - } - - private String stringifyServerPacket(ByteBuffer packet) { - PacketDecoder decoder = decode(packet); - if (!decoder.isControlPacket()) - return stringifyDataPacket("SERVER", decoder); - switch (decoder.opCode()) { - case SocketTunnelBase.SERVER_OPEN_ACK: - return "SERVER_OPEN_ACK " + Integer.valueOf(decoder.connectionId()); - case SocketTunnelBase.SERVER_CLOSE: - return "SERVER_CLOSE " + Integer.valueOf(decoder.connectionId()); - default: - throw new RuntimeException("Invalid packet"); - } - } - - private PacketDecoder decode(ByteBuffer packet) { - int position = packet.position(); - packet.position(0); - if (position == 0) { - throw new RuntimeException("Empty packet"); - } - PacketDecoder decoder = PacketDecoder.decode(packet); - packet.position(position); - return decoder; - } -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/PacketDecoder.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/PacketDecoder.java deleted file mode 100644 index d8e0dff..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/PacketDecoder.java +++ /dev/null @@ -1,67 +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; - -/** - * Decodes data packets of SocketTunnelClient and SocketTunnelServer for tests. - */ -public final class PacketDecoder extends SocketTunnelBase.PacketDecoderBase { - private boolean mControlPacket = false; - private boolean mDataPacket; - private int mOpCode; - private int mConnectionId; - private byte[] mData; - - protected void onReceivedDataPacket(int connectionId, byte[] data) { - mDataPacket = true; - mConnectionId = connectionId; - mData = data; - } - - @Override - protected void onReceivedControlPacket(int connectionId, byte opCode) { - mControlPacket = true; - mOpCode = opCode; - mConnectionId = connectionId; - } - - public static PacketDecoder tryDecode(ByteBuffer packet) throws SocketTunnelBase.ProtocolError { - PacketDecoder decoder = new PacketDecoder(); - decoder.decodePacket(packet); - return decoder; - } - - public static PacketDecoder decode(ByteBuffer packet) { - try { - return tryDecode(packet); - } catch (SocketTunnelBase.ProtocolError e) { - throw new RuntimeException(e); - } - } - - public boolean isControlPacket() { - return mControlPacket; - } - - public boolean isDataPacket() { - return mDataPacket; - } - - public int opCode() { - assert isControlPacket(); - return mOpCode; - } - - public int connectionId() { - return mConnectionId; - } - - public byte[] data() { - assert isDataPacket(); - return mData.clone(); - } -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SignalingThreadMock.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SignalingThreadMock.java deleted file mode 100644 index 16e0973..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SignalingThreadMock.java +++ /dev/null @@ -1,162 +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.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Convinience class for tests. Like WebRTC threads supports posts - * and synchromous invokes. - */ -class SignalingThreadMock { - // TODO: use scaleTimeout when natives for org.chromium.base get available. - private static final int EXECUTION_TIME_LIMIT_MS = 5000; - - private final AtomicInteger mInvokationCounter = new AtomicInteger(0); - private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - private final ScheduledExecutorService mWatchDogExecutor = - Executors.newSingleThreadScheduledExecutor(); - private ScheduledFuture<?> mWatchDogFuture; - private final Thread mThread; - private final BlockingQueue<Runnable> mExecutionQueue = new LinkedBlockingDeque<Runnable>(); - - public SignalingThreadMock() { - mThread = new Thread() { - @Override - public void run() { - try { - runExecutionLoop(); - } catch (InterruptedException e) { - // Normal finish. - } - } - }; - mThread.start(); - } - - private void runExecutionLoop() throws InterruptedException { - while (true) { - mExecutionQueue.take().run(); - } - } - - public void invoke(final Runnable runnable) { - try { - invoke(new TestUtils.RunnableAdapter(runnable)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public <T> T invoke(final Callable<T> callable) throws Exception { - if (isOnThread()) return callable.call(); - - try { - return new InvokeWrapper<T>(callable).invoke(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throw (Exception) e.getCause(); - } - } - - public void post(Runnable runnable) { - boolean success = mExecutionQueue.offer(new PostWrapper(runnable)); - assert success; - } - - public void dispose() { - mWatchDogExecutor.shutdown(); - mThread.interrupt(); - try { - mThread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - public boolean isOnThread() { - return Thread.currentThread() == mThread; - } - - private void onStartedExecution(final int index, final Exception timeoutException) { - mWatchDogFuture = mWatchDogExecutor.schedule(new Runnable() { - @Override - public void run() { - throw new RuntimeException( - "Time limit on " + Integer.toString(index) + " invocation", - timeoutException); - } - }, EXECUTION_TIME_LIMIT_MS, TimeUnit.MILLISECONDS); - } - - private void onFinishedExecution() { - mWatchDogFuture.cancel(false); - } - - private abstract class WrapperBase implements Runnable { - private final int mIndex; - private final Exception mTimeoutException; - - protected WrapperBase() { - mIndex = mInvokationCounter.incrementAndGet(); - mTimeoutException = new Exception("Timeout exception"); - } - - @Override - public final void run() { - onStartedExecution(mIndex, mTimeoutException); - try { - runWrapped(); - } finally { - onFinishedExecution(); - } - } - - protected abstract void runWrapped(); - } - - private class InvokeWrapper<T> extends WrapperBase { - private final Callable<T> mWrapped; - private final TestUtils.InvokeHelper<T> mHelper = new TestUtils.InvokeHelper<T>(); - - public InvokeWrapper(Callable<T> wrapped) { - mWrapped = wrapped; - } - - @Override - protected void runWrapped() { - mHelper.runOnTargetThread(mWrapped); - } - - public T invoke() throws Exception { - boolean success = mExecutionQueue.offer(this); - assert success; - return mHelper.takeResult(); - } - } - - private class PostWrapper extends WrapperBase { - private final Runnable mWrapped; - - public PostWrapper(Runnable wrapped) { - mWrapped = wrapped; - } - - @Override - protected void runWrapped() { - mWrapped.run(); - } - } -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelClient.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelClient.java deleted file mode 100644 index 4a83386..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelClient.java +++ /dev/null @@ -1,303 +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.util.Log; - -import java.io.IOException; -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; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Listens LocalServerSocket and tunnels all connections to the SocketTunnelServer. - */ -public class SocketTunnelClient extends SocketTunnelBase { - private static final String TAG = "SocketTunnelClient"; - - private enum State { - INITIAL, RUNNING, STOPPED - } - - private final AtomicReference<State> mState = new AtomicReference<State>(State.INITIAL); - - private final LocalServerSocket mSocket; - private final ExecutorService mThreadPool = Executors.newCachedThreadPool(); - - // Connections with opened server to client stream. Always accesses on signaling thread. - private final Map<Integer, Connection> mServerConnections = - new HashMap<Integer, Connection>(); - - // Accepted connections are kept here until server returns SERVER_OPEN_ACK or SERVER_CLOSE. - // New connections are added in the listening loop, checked and removed on signaling thread. - // So add/read/remove synchronized through message round trip. - private final ConcurrentMap<Integer, Connection> mPendingConnections = - new ConcurrentHashMap<Integer, Connection>(); - - private final IdRegistry mIdRegistry = new IdRegistry(MIN_CONNECTION_ID, MAX_CONNECTION_ID, 2); - - /** - * This class responsible for generating valid connection IDs. It count usage of connection: - * one user for client to server stream and one for server to client one. When both are closed - * it's safe to reuse ID. - */ - private static final class IdRegistry { - private final int[] mLocks; - private final int mMin; - private final int mMax; - private final int mMaxLocks; - private final Object mLock = new Object(); - - public IdRegistry(int minId, int maxId, int maxLocks) { - assert minId < maxId; - assert maxLocks > 0; - - mMin = minId; - mMax = maxId; - mMaxLocks = maxLocks; - mLocks = new int[maxId - minId + 1]; - } - - public void lock(int id) { - synchronized (mLock) { - int index = toIndex(id); - if (mLocks[index] == 0 || mLocks[index] == mMaxLocks) { - throw new RuntimeException(); - } - mLocks[index]++; - } - } - - public void release(int id) { - synchronized (mLock) { - int index = toIndex(id); - if (mLocks[index] == 0) { - throw new RuntimeException("Releasing unlocked id " + Integer.toString(id)); - } - mLocks[index]--; - } - } - - public boolean isLocked(int id) { - synchronized (mLock) { - return mLocks[toIndex(id)] > 0; - } - } - - public int generate() throws NoIdAvailableException { - synchronized (mLock) { - for (int id = mMin; id != mMax; id++) { - int index = toIndex(id); - if (mLocks[index] == 0) { - mLocks[index] = 1; - return id; - } - } - } - throw new NoIdAvailableException(); - } - - private int toIndex(int id) { - if (id < mMin || id > mMax) { - throw new RuntimeException(); - } - return id - mMin; - } - } - - private static class NoIdAvailableException extends Exception {} - - public SocketTunnelClient(String socketName) throws IOException { - mSocket = new LocalServerSocket(socketName); - } - - public boolean hasConnections() { - return mServerConnections.size() + mPendingConnections.size() > 0; - } - - @Override - public AbstractDataChannel unbind() { - AbstractDataChannel dataChannel = super.unbind(); - close(); - return dataChannel; - } - - public void close() { - if (mState.get() != State.STOPPED) closeSocket(); - } - - @Override - protected void onReceivedDataPacket(int connectionId, byte[] data) throws ProtocolError { - checkCalledOnSignalingThread(); - - if (!mServerConnections.containsKey(connectionId)) - throw new ProtocolError("Unknows connection id"); - - mServerConnections.get(connectionId).onReceivedDataPacket(data); - } - - @Override - protected void onReceivedControlPacket(int connectionId, byte opCode) throws ProtocolError { - switch (opCode) { - case SERVER_OPEN_ACK: - onServerOpenAck(connectionId); - break; - - case SERVER_CLOSE: - onServerClose(connectionId); - break; - - default: - throw new ProtocolError("Invalid opCode"); - } - } - - private void onServerOpenAck(int connectionId) throws ProtocolError { - checkCalledOnSignalingThread(); - - if (mServerConnections.containsKey(connectionId)) { - throw new ProtocolError("Connection already acknowledged"); - } - - if (!mPendingConnections.containsKey(connectionId)) { - throw new ProtocolError("Unknow connection id"); - } - - // Check/get is safe since it can be only removed on this thread. - Connection connection = mPendingConnections.get(connectionId); - mPendingConnections.remove(connectionId); - - mServerConnections.put(connectionId, connection); - - // Lock for client to server stream. - mIdRegistry.lock(connectionId); - mThreadPool.execute(connection); - } - - private void onServerClose(int connectionId) throws ProtocolError { - checkCalledOnSignalingThread(); - - if (mServerConnections.containsKey(connectionId)) { - Connection connection = mServerConnections.get(connectionId); - mServerConnections.remove(connectionId); - mIdRegistry.release(connectionId); // Release sever to client stream. - connection.closedByServer(); - } else if (mPendingConnections.containsKey(connectionId)) { - Connection connection = mPendingConnections.get(connectionId); - mPendingConnections.remove(connectionId); - connection.closedByServer(); - sendToDataChannel(buildControlPacket(connectionId, CLIENT_CLOSE)); - mIdRegistry.release(connectionId); // Release sever to client stream. - } else { - throw new ProtocolError("Closing unknown connection"); - } - } - - @Override - protected void onDataChannelOpened() { - if (!mState.compareAndSet(State.INITIAL, State.RUNNING)) { - throw new InvalidStateException(); - } - - mThreadPool.execute(new Runnable() { - @Override - public void run() { - runListenLoop(); - } - }); - } - - @Override - protected void onDataChannelClosed() { - // All new connections will be rejected. - if (!mState.compareAndSet(State.RUNNING, State.STOPPED)) { - throw new InvalidStateException(); - } - - for (Connection connection : mServerConnections.values()) { - connection.terminate(); - } - - for (Connection connection : mPendingConnections.values()) { - connection.terminate(); - } - - closeSocket(); - - mThreadPool.shutdown(); - } - - private void closeSocket() { - try { - mSocket.close(); - } catch (IOException e) { - Log.d(TAG, "Failed to close socket: " + e); - onSocketException(e, -1); - } - } - - private void runListenLoop() { - try { - while (true) { - LocalSocket socket = mSocket.accept(); - State state = mState.get(); - if (mState.get() == State.RUNNING) { - // Make sure no socket processed when stopped. - clientOpenConnection(socket); - } else { - socket.close(); - } - } - } catch (IOException e) { - if (mState.get() != State.RUNNING) { - onSocketException(e, -1); - } - // Else exception expected (socket closed). - } - } - - private void clientOpenConnection(LocalSocket socket) throws IOException { - try { - int id = mIdRegistry.generate(); // id generated locked for server to client stream. - Connection connection = new Connection(id, socket); - mPendingConnections.put(id, connection); - sendToDataChannel(buildControlPacket(id, CLIENT_OPEN)); - } catch (NoIdAvailableException e) { - socket.close(); - } - } - - private final class Connection extends ConnectionBase implements Runnable { - public Connection(int id, LocalSocket socket) { - super(id, socket); - } - - public void closedByServer() { - shutdownOutput(); - } - - @Override - public void run() { - assert mIdRegistry.isLocked(mId); - - runReadingLoop(); - - shutdownInput(); - sendToDataChannel(buildControlPacket(mId, CLIENT_CLOSE)); - mIdRegistry.release(mId); // Unlock for client to server stream. - } - } - - /** - * Method called in inappropriate state. - */ - public static class InvalidStateException extends RuntimeException {} -} diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/TestUtils.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/TestUtils.java deleted file mode 100644 index 81f7a44..0000000 --- a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/TestUtils.java +++ /dev/null @@ -1,134 +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.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -/** - * Utilities to testing a socket tunnel. - */ -public class TestUtils { - private static final String CHARSET = "UTF-8"; - - // Sends |request| string to UNIX socket socketName on another thread and returns - // Future<String> for obtainings response. - public static Future<String> asyncRequest(final String socketName, final String request) { - - final ExecutorService executor = Executors.newSingleThreadExecutor(); - - return executor.submit(new Callable<String>() { - @Override - public String call() throws Exception { - LocalSocket socket = new LocalSocket(); - socket.connect(new LocalSocketAddress(socketName)); - writeAndShutdown(socket, request); - - String response = readAll(socket); - socket.close(); - - executor.shutdown(); - return response; - } - }); - } - - public static void writeAndShutdown(LocalSocket socket, String data) throws IOException { - socket.getOutputStream().write(data.getBytes(CHARSET)); - socket.getOutputStream().flush(); - socket.shutdownOutput(); - } - - // Reads all bytes from socket input stream until EOF and converts it to UTF-8 string. - public static String readAll(LocalSocket socket) throws IOException { - byte[] buffer = new byte[1000]; - int position = 0; - while (true) { - int count = socket.getInputStream().read(buffer, position, buffer.length - position); - if (count == -1) - break; - position += count; - } - return new String(buffer, 0, position, CHARSET); - } - - /** - * Utility class for thread synchronization. Allow to track moving through series of steps. - */ - public static class StateBarrier<T> { - private T mState; - private final Object mLock = new Object(); - - public StateBarrier(T initialState) { - mState = initialState; - } - - // Waits until state |from| reached and change state to |to|. - public void advance(T from, T to) { - synchronized (mLock) { - while (mState.equals(from)) { - try { - mLock.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - mState = to; - mLock.notifyAll(); - } - } - } - - /** - * Helper with runs code on another thread and synchronously take the result. - */ - public static class InvokeHelper<T> { - private final CountDownLatch mDone = new CountDownLatch(1); - - private T mResult = null; - private Exception mException = null; - - public void runOnTargetThread(Callable<T> callable) { - try { - mResult = callable.call(); - } catch (Exception e) { - mException = e; - } - mDone.countDown(); - } - - public T takeResult() throws Exception { - mDone.await(); - if (mException != null) - throw mException; - else - return mResult; - } - } - - /** - * Adapts Runnable to Callable<Void>. - */ - public static class RunnableAdapter implements Callable<Void> { - private final Runnable mAdaptee; - - public RunnableAdapter(Runnable adaptee) { - mAdaptee = adaptee; - } - - @Override - public Void call() { - mAdaptee.run(); - return null; - } - } -} |