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            throws IOException {
98        mRemoteMachineIpAddress = remoteMachineIp;
99        mPort = port;
100
101        mFromSocket = fromSocket;
102        mToSocket = toSocket;
103
104        try {
105            mFromSocketInputStream = mFromSocket.getInputStream();
106            mToSocketInputStream = mToSocket.getInputStream();
107            mFromSocketOutputStream = mFromSocket.getOutputStream();
108            mToSocketOutputStream = mToSocket.getOutputStream();
109            AdbUtils.configureConnection(mToSocketInputStream, mToSocketOutputStream,
110                    mRemoteMachineIpAddress, mPort);
111        } catch (IOException e) {
112            Log.e(LOG_TAG, "Unable to start ConnectionHandler", e);
113            closeStreams();
114            throw e;
115        }
116
117        mFromToPipe = new SocketPipeThread(mFromSocketInputStream, mToSocketOutputStream);
118        mToFromPipe = new SocketPipeThread(mToSocketInputStream, mFromSocketOutputStream);
119    }
120
121    public void registerOnConnectionHandlerFinishedCallback(OnFinishedCallback callback) {
122        mOnFinishedCallback = callback;
123    }
124
125    private void closeStreams() {
126        FsUtils.closeInputStream(mFromSocketInputStream);
127        FsUtils.closeInputStream(mToSocketInputStream);
128        FsUtils.closeOutputStream(mFromSocketOutputStream);
129        FsUtils.closeOutputStream(mToSocketOutputStream);
130    }
131
132    public void start() {
133        /** We have 2 threads running, one for each pipe, that we start here. */
134        mThreadsRunning = 2;
135        mFromToPipe.start();
136        mToFromPipe.start();
137    }
138
139    public void stop() {
140        shutdown(mFromSocket);
141        shutdown(mToSocket);
142    }
143
144    private void shutdown(Socket socket) {
145        synchronized (mFromToPipe) {
146            synchronized (mToFromPipe) {
147                /** This will stop the while loop in the run method */
148                try {
149                    if (!socket.isInputShutdown()) {
150                        socket.shutdownInput();
151                    }
152                } catch (IOException e) {
153                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
154                }
155                try {
156                    if (!socket.isOutputShutdown()) {
157                        socket.shutdownOutput();
158                    }
159                } catch (IOException e) {
160                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
161                }
162                try {
163                    if (!socket.isClosed()) {
164                        socket.close();
165                    }
166                } catch (IOException e) {
167                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
168                }
169            }
170        }
171    }
172}
173