ConnectionHandler.java revision 5af84db492a0c198377ba4dacc83c5a211e96ff6
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dumprendertree2.forwarder;
18
19import android.util.Log;
20
21import com.android.dumprendertree2.FsUtils;
22
23import java.io.IOException;
24import java.io.InputStream;
25import java.io.OutputStream;
26import java.net.Socket;
27
28/**
29 * Worker class for {@link Forwarder}. A ConnectionHandler will be created once the Forwarder
30 * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
31 * connection already proxied by adb networking (see also {@link AdbUtils}).
32 */
33public class ConnectionHandler {
34
35    private static final String LOG_TAG = "ConnectionHandler";
36
37    public static interface OnFinishedCallback {
38        public void onFinished();
39    }
40
41    private class SocketPipeThread extends Thread {
42
43        private InputStream mInputStream;
44        private OutputStream mOutputStream;
45
46        public SocketPipeThread(InputStream inputStream, OutputStream outputStream) {
47            mInputStream = inputStream;
48            mOutputStream = outputStream;
49            setName("SocketPipeThread: " + getName());
50        }
51
52        @Override
53        public void run() {
54            byte[] buffer = new byte[4096];
55            int length;
56            while (true) {
57                try {
58                    if ((length = mInputStream.read(buffer)) < 0) {
59                        break;
60                    }
61                    mOutputStream.write(buffer, 0, length);
62                } catch (IOException e) {
63                    /** This exception means one of the streams is closed */
64                    Log.v(LOG_TAG, this.toString(), e);
65                    break;
66                }
67            }
68
69            synchronized (mThreadsRunning) {
70                mThreadsRunning--;
71                if (mThreadsRunning == 0) {
72                    ConnectionHandler.this.stop();
73                    mOnFinishedCallback.onFinished();
74                }
75            }
76        }
77
78        @Override
79        public String toString() {
80            return getName();
81        }
82    }
83
84    private Integer mThreadsRunning;
85
86    private Socket mFromSocket, mToSocket;
87    private SocketPipeThread mFromToPipe, mToFromPipe;
88    private InputStream mFromSocketInputStream, mToSocketInputStream;
89    private OutputStream mFromSocketOutputStream, mToSocketOutputStream;
90
91    private int mPort;
92    private String mRemoteMachineIpAddress;
93
94    private OnFinishedCallback mOnFinishedCallback;
95
96    public ConnectionHandler(String remoteMachineIp, int port, Socket fromSocket, Socket toSocket) {
97        mRemoteMachineIpAddress = remoteMachineIp;
98        mPort = port;
99
100        mFromSocket = fromSocket;
101        mToSocket = toSocket;
102
103        try {
104            mFromSocketInputStream = mFromSocket.getInputStream();
105            mToSocketInputStream = mToSocket.getInputStream();
106            mFromSocketOutputStream = mFromSocket.getOutputStream();
107            mToSocketOutputStream = mToSocket.getOutputStream();
108            if (!AdbUtils.configureConnection(mToSocketInputStream, mToSocketOutputStream,
109                    mRemoteMachineIpAddress, mPort)) {
110                throw new IOException("Configuring socket failed!");
111            }
112        } catch (IOException e) {
113            Log.e(LOG_TAG, "Unable to start ConnectionHandler", e);
114            closeStreams();
115            return;
116        }
117
118        mFromToPipe = new SocketPipeThread(mFromSocketInputStream, mToSocketOutputStream);
119        mToFromPipe = new SocketPipeThread(mToSocketInputStream, mFromSocketOutputStream);
120    }
121
122    public void registerOnConnectionHandlerFinishedCallback(OnFinishedCallback callback) {
123        mOnFinishedCallback = callback;
124    }
125
126    private void closeStreams() {
127        FsUtils.closeInputStream(mFromSocketInputStream);
128        FsUtils.closeInputStream(mToSocketInputStream);
129        FsUtils.closeOutputStream(mFromSocketOutputStream);
130        FsUtils.closeOutputStream(mToSocketOutputStream);
131    }
132
133    public void start() {
134        /** We have 2 threads running, one for each pipe, that we start here. */
135        mThreadsRunning = 2;
136        mFromToPipe.start();
137        mToFromPipe.start();
138    }
139
140    public void stop() {
141        shutdown(mFromSocket);
142        shutdown(mToSocket);
143    }
144
145    private void shutdown(Socket socket) {
146        synchronized (mFromToPipe) {
147            synchronized (mToFromPipe) {
148                /** This will stop the while loop in the run method */
149                try {
150                    if (!socket.isInputShutdown()) {
151                        socket.shutdownInput();
152                    }
153                } catch (IOException e) {
154                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
155                }
156                try {
157                    if (!socket.isOutputShutdown()) {
158                        socket.shutdownOutput();
159                    }
160                } catch (IOException e) {
161                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
162                }
163                try {
164                    if (!socket.isClosed()) {
165                        socket.close();
166                    }
167                } catch (IOException e) {
168                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
169                }
170            }
171        }
172    }
173}