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 /components/devtools_bridge/android | |
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}
Diffstat (limited to 'components/devtools_bridge/android')
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(); - } -} - |