diff options
| author | Maksymilian Osowski <maxosowski@google.com> | 2010-08-26 06:21:58 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-08-26 06:21:58 -0700 |
| commit | 7f335371dd23580fc748c1fe7b5578df9e75174e (patch) | |
| tree | c08b3eac5e9680d350d8e0bd594fd6ee3aaaf211 /tests | |
| parent | e9ddcba348ee45fe000d84efaad98484db032926 (diff) | |
| parent | 56d7e400ece64591685c8a21dbb82a94a7bd8010 (diff) | |
| download | frameworks_base-7f335371dd23580fc748c1fe7b5578df9e75174e.zip frameworks_base-7f335371dd23580fc748c1fe7b5578df9e75174e.tar.gz frameworks_base-7f335371dd23580fc748c1fe7b5578df9e75174e.tar.bz2 | |
Merge "Added forwarding service."
Diffstat (limited to 'tests')
4 files changed, 407 insertions, 0 deletions
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java new file mode 100644 index 0000000..d165a1a --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.forwarder; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * The utility class that can setup a socket allowing the device to communicate with remote + * machines through the machine that the device is connected to via adb. + */ +public class AdbUtils { + private static final String LOG_TAG = "AdbUtils"; + + private static final String ADB_OK = "OKAY"; + private static final int ADB_PORT = 5037; + private static final String ADB_HOST = "127.0.0.1"; + private static final int ADB_RESPONSE_SIZE = 4; + + /** + * Send an ADB command using existing socket connection + * + * The streams provided must be from a socket connected to adb already + * + * @param is input stream of the socket connection + * @param os output stream of the socket + * @param cmd the adb command to send + * @return if adb gave a success response + * @throws IOException + */ + private static boolean sendAdbCmd(InputStream is, OutputStream os, String cmd) + throws IOException { + byte[] buf = new byte[ADB_RESPONSE_SIZE]; + + cmd = String.format("%04X", cmd.length()) + cmd; + os.write(cmd.getBytes()); + int read = is.read(buf); + if (read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) { + Log.w(LOG_TAG, "adb cmd faild."); + return false; + } + return true; + } + + /** + * Get a tcp socket connection to specified IP address and port proxied by adb + * + * The proxying is transparent, e.g. if a socket is returned, then it can be written to and + * read from as if it is directly connected to the target + * + * @param remoteAddress IP address of the host to connect to + * @param remotePort port of the host to connect to + * @return a valid Socket instance if successful, null otherwise + */ + public static Socket getSocketToRemoteMachine(String remoteAddress, int remotePort) { + try { + Socket socket = new Socket(ADB_HOST, ADB_PORT); + String cmd = "tcp:" + remotePort + ":" + remoteAddress; + if (!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) { + socket.close(); + return null; + } + return socket; + } catch (IOException ioe) { + Log.w(LOG_TAG, "error creating adb socket", ioe); + return null; + } + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java new file mode 100644 index 0000000..6a6cb7f --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.forwarder; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * Worker class for {@link Forwarder}. A ConnectionHandler will be created once the Forwarder + * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a + * connection already proxied by adb networking (see also {@link AdbUtils}). + */ +public class ConnectionHandler { + + private static final String LOG_TAG = "ConnectionHandler"; + + private class SocketPipeThread extends Thread { + + private Socket mInSocket, mOutSocket; + + public SocketPipeThread(Socket inSocket, Socket outSocket) { + mInSocket = inSocket; + mOutSocket = outSocket; + } + + @Override + public void run() { + InputStream is; + OutputStream os; + try { + synchronized (this) { + is = mInSocket.getInputStream(); + os = mOutSocket.getOutputStream(); + } + } catch (IOException e) { + Log.w(LOG_TAG, this.toString(), e); + return; + } + + byte[] buffer = new byte[4096]; + int length; + while (true) { + try { + synchronized (this) { + if ((length = is.read(buffer)) <= 0) { + break; + } + os.write(buffer, 0, length); + } + } catch (IOException e) { + /** This exception means one of the streams is closed */ + Log.v(LOG_TAG, this.toString(), e); + break; + } + } + } + + @Override + public String toString() { + return "SocketPipeThread:\n" + mInSocket + "\n=>\n" + mOutSocket; + } + } + + private Socket mFromSocket, mToSocket; + private SocketPipeThread mFromToPipe, mToFromPipe; + + public ConnectionHandler(Socket fromSocket, Socket toSocket) { + mFromSocket = fromSocket; + mToSocket = toSocket; + mFromToPipe = new SocketPipeThread(mFromSocket, mToSocket); + mToFromPipe = new SocketPipeThread(mToSocket, mFromSocket); + } + + public void start() { + mFromToPipe.start(); + mToFromPipe.start(); + } + + public void stop() { + shutdown(mFromSocket); + shutdown(mToSocket); + } + + private void shutdown(Socket socket) { + try { + synchronized (mFromToPipe) { + synchronized (mToFromPipe) { + /** This will stop the while loop in the run method */ + socket.shutdownInput(); + socket.shutdownOutput(); + socket.close(); + } + } + } catch (IOException e) { + Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e); + } + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java new file mode 100644 index 0000000..e5ef6da --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.forwarder; + +import android.util.Log; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashSet; +import java.util.Set; + +/** + * A port forwarding server. Listens on localhost on specified port and forwards the tcp + * communications to external socket via adb networking proxy. + */ +public class Forwarder extends Thread { + private static final String LOG_TAG = "Forwarder"; + + private int mPort; + private String mRemoteMachineIpAddress; + + private Boolean mIsRunning = false; + private ServerSocket mServerSocket; + + private Set<ConnectionHandler> mConnectionHandlers = new HashSet<ConnectionHandler>(); + + public Forwarder(int port, String remoteMachineIpAddress) { + mPort = port; + mRemoteMachineIpAddress = remoteMachineIpAddress; + } + + @Override + public void start() { + Log.i(LOG_TAG, "start(): Starting fowarder on port: " + mPort); + synchronized (this) { + if (mIsRunning) { + Log.w(LOG_TAG, "start(): Forwarder on port: " + mPort + " already running! NOOP."); + return; + } + } + + try { + mServerSocket = new ServerSocket(mPort); + } catch (IOException e) { + Log.e(LOG_TAG, "mPort=" + mPort, e); + return; + } + + mIsRunning = true; + super.start(); + } + + @Override + public void run() { + while (true) { + synchronized (this) { + if (!mIsRunning) { + return; + } + + /** These sockets will be closed when Forwarder.stop() is called */ + Socket localSocket; + Socket remoteSocket; + try { + localSocket = mServerSocket.accept(); + remoteSocket = AdbUtils.getSocketToRemoteMachine(mRemoteMachineIpAddress, + mPort); + } catch (IOException e) { + /** This most likely means that mServerSocket is already closed */ + Log.w(LOG_TAG + "mPort=" + mPort, e); + return; + } + + if (remoteSocket == null) { + try { + localSocket.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "mPort=" + mPort, e); + } + + Log.e(LOG_TAG, "run(): mPort= " + mPort + " Failed to start forwarding from " + + localSocket); + continue; + } + + ConnectionHandler forwarder = new ConnectionHandler(localSocket, remoteSocket); + mConnectionHandlers.add(forwarder); + forwarder.start(); + + } + } + } + + public void finish() { + synchronized (this) { + if (!mIsRunning) { + return; + } + } + + try { + mServerSocket.close(); + } catch (IOException e) { + Log.e(LOG_TAG, "mPort=" + mPort, e); + } + + synchronized (this) { + mIsRunning = false; + } + + for (ConnectionHandler connectionHandler : mConnectionHandlers) { + connectionHandler.stop(); + } + mConnectionHandlers.clear(); + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java new file mode 100644 index 0000000..10fee4b --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.forwarder; + +import java.util.HashSet; +import java.util.Set; + +/** + * A simple class to start and stop Forwarders running on some ports. + * + * It uses a singleton pattern and is thread safe. + */ +public class ForwarderManager { + /** + * The IP address of the server serving the tests. + */ + private static final String HOST_IP = "127.0.0.1"; + + /** + * We use these ports because other webkit platforms do. They are set up in + * external/webkit/LayoutTests/http/conf/apache2-debian-httpd.conf + */ + public static final int HTTP_PORT = 8080; + public static final int HTTPS_PORT = 8443; + + private static ForwarderManager forwarderManager; + + private Set<Forwarder> mServers; + + private ForwarderManager() { + mServers = new HashSet<Forwarder>(2); + mServers.add(new Forwarder(HTTP_PORT, HOST_IP)); + mServers.add(new Forwarder(HTTPS_PORT, HOST_IP)); + } + + public static synchronized ForwarderManager getForwarderManager() { + if (forwarderManager == null) { + forwarderManager = new ForwarderManager(); + } + return forwarderManager; + } + + @Override + public Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + public synchronized void start() { + for (Forwarder server : mServers) { + server.start(); + } + } + + public synchronized void stop() { + for (Forwarder server : mServers) { + server.finish(); + } + } +}
\ No newline at end of file |
