1da205a749fadb3a87357d9bd607f094c7717764aJason Monk/**
2da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Copyright (c) 2013, The Android Open Source Project
3da205a749fadb3a87357d9bd607f094c7717764aJason Monk *
4da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Licensed under the Apache License, Version 2.0 (the "License");
5da205a749fadb3a87357d9bd607f094c7717764aJason Monk * you may not use this file except in compliance with the License.
6da205a749fadb3a87357d9bd607f094c7717764aJason Monk * You may obtain a copy of the License at
7da205a749fadb3a87357d9bd607f094c7717764aJason Monk *
8da205a749fadb3a87357d9bd607f094c7717764aJason Monk *     http://www.apache.org/licenses/LICENSE-2.0
9da205a749fadb3a87357d9bd607f094c7717764aJason Monk *
10da205a749fadb3a87357d9bd607f094c7717764aJason Monk * Unless required by applicable law or agreed to in writing, software
11da205a749fadb3a87357d9bd607f094c7717764aJason Monk * distributed under the License is distributed on an "AS IS" BASIS,
12da205a749fadb3a87357d9bd607f094c7717764aJason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13da205a749fadb3a87357d9bd607f094c7717764aJason Monk * See the License for the specific language governing permissions and
14da205a749fadb3a87357d9bd607f094c7717764aJason Monk * limitations under the License.
15da205a749fadb3a87357d9bd607f094c7717764aJason Monk */
16602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpackage com.android.proxyhandler;
17602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
186f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport android.net.ProxyProperties;
196f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport android.os.RemoteException;
20602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport android.util.Log;
21602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
226f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monkimport com.android.net.IProxyPortListener;
23602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport com.google.android.collect.Lists;
24602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
25602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.IOException;
26602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.InputStream;
27602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.io.OutputStream;
28fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monkimport java.net.InetAddress;
29602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.InetSocketAddress;
30602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Proxy;
31602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.ProxySelector;
32602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.ServerSocket;
33602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.Socket;
34602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.SocketException;
35602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URI;
36602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.net.URISyntaxException;
37602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.List;
38602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.concurrent.ExecutorService;
39602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkimport java.util.concurrent.Executors;
40602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
41602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk/**
42602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk * @hide
43602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk */
44602b232a06ede86999aa362a12eb28cbc782dc1dJason Monkpublic class ProxyServer extends Thread {
45602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
46602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final String CONNECT = "CONNECT";
47602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final String HTTP_OK = "HTTP/1.1 200 OK\n";
48602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
49602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private static final String TAG = "ProxyServer";
50602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
51602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private ExecutorService threadExecutor;
52602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
53602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public boolean mIsRunning = false;
54602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
55602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private ServerSocket serverSocket;
566f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    private int mPort;
576f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    private IProxyPortListener mCallback;
58602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
59602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    private class ProxyConnection implements Runnable {
60602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        private Socket connection;
61602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
62602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        private ProxyConnection(Socket connection) {
63602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            this.connection = connection;
64602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
65602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
66602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        @Override
67602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        public void run() {
68602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            try {
69602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                String requestLine = getLine(connection.getInputStream());
70602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                if (requestLine == null) {
71602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    connection.close();
72602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    return;
73602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
74602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                String[] splitLine = requestLine.split(" ");
75602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                if (splitLine.length < 3) {
76602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    connection.close();
77602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    return;
78602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
79602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                String requestType = splitLine[0];
80602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                String urlString = splitLine[1];
81602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
82602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                String host = "";
83602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                int port = 80;
84602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
85602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                if (requestType.equals(CONNECT)) {
86602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    String[] hostPortSplit = urlString.split(":");
87602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    host = hostPortSplit[0];
88602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    try {
89602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        port = Integer.parseInt(hostPortSplit[1]);
90602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    } catch (NumberFormatException nfe) {
91602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        port = 443;
92602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    }
93602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    urlString = "Https://" + host + ":" + port;
94602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                } else {
95602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    try {
96602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        URI url = new URI(urlString);
97602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        host = url.getHost();
98602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        port = url.getPort();
99602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        if (port < 0) {
100602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            port = 80;
101602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        }
102602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    } catch (URISyntaxException e) {
103602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        connection.close();
104602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        return;
105602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    }
106602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
107602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
108602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                List<Proxy> list = Lists.newArrayList();
109602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                try {
110602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    list = ProxySelector.getDefault().select(new URI(urlString));
111602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                } catch (URISyntaxException e) {
112602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    e.printStackTrace();
113602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
114602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                Socket server = null;
115602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                for (Proxy proxy : list) {
116602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    try {
117602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        if (!proxy.equals(Proxy.NO_PROXY)) {
118602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            // Only Inets created by PacProxySelector.
119602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            InetSocketAddress inetSocketAddress =
120602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                    (InetSocketAddress)list.get(0).address();
121602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            server = new Socket(inetSocketAddress.getAddress(),
122602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                    inetSocketAddress.getPort());
123602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            sendLine(server, requestLine);
124602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        } else {
125602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            server = new Socket(host, port);
126602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            if (requestType.equals(CONNECT)) {
127602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                while (getLine(connection.getInputStream()).length() != 0);
128602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                // No proxy to respond so we must.
129602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                sendLine(connection, HTTP_OK);
130602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            } else {
131602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                                sendLine(server, requestLine);
132602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                            }
133602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        }
134602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    } catch (IOException ioe) {
135602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
136602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    }
137602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    if (server != null) {
138602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        break;
139602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    }
140602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
141602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                if (server == null) {
142602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    server = new Socket(host, port);
143602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    if (requestType.equals(CONNECT)) {
144602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        while (getLine(connection.getInputStream()).length() != 0);
145602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        // No proxy to respond so we must.
146602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        sendLine(connection, HTTP_OK);
147602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    } else {
148602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                        sendLine(server, requestLine);
149602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    }
150602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
151602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                // Pass data back and forth until complete.
152602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                SocketConnect.connect(connection, server);
153602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            } catch (IOException e) {
154602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                Log.d(TAG, "Problem Proxying", e);
155602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
156602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            try {
157602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                connection.close();
158602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            } catch (IOException ioe) {
159602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
160602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
161602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
162602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
163602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        private String getLine(InputStream inputStream) throws IOException {
164602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            StringBuffer buffer = new StringBuffer();
165602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            int byteBuffer = inputStream.read();
166602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            if (byteBuffer < 0) return "";
167602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            do {
168602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                if (byteBuffer != '\r') {
169602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                    buffer.append((char)byteBuffer);
170602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
171602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                byteBuffer = inputStream.read();
172602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            } while ((byteBuffer != '\n') && (byteBuffer >= 0));
173602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
174602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            return buffer.toString();
175602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
176602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
177602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        private void sendLine(Socket socket, String line) throws IOException {
178602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            OutputStream os = socket.getOutputStream();
179602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            os.write(line.getBytes());
180602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            os.write('\r');
181602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            os.write('\n');
182602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            os.flush();
183602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
184602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
185602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
186602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public ProxyServer() {
187602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        threadExecutor = Executors.newCachedThreadPool();
1886f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        mPort = -1;
1896f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        mCallback = null;
190602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
191602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
192602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    @Override
193602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public void run() {
194602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        try {
1956f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            serverSocket = new ServerSocket(0);
196602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
1976f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            if (serverSocket != null) {
1986f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                setPort(serverSocket.getLocalPort());
199602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
2006f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                while (mIsRunning) {
2016f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    try {
202fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                        Socket socket = serverSocket.accept();
203fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                        // Only receive local connections.
204fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                        if (socket.getInetAddress().isLoopbackAddress()) {
205fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                            ProxyConnection parser = new ProxyConnection(socket);
206602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
207fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                            threadExecutor.execute(parser);
208fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                        } else {
209fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                            socket.close();
210fa4518f7632f0a2146a0638cf39d6d9e1d832a01Jason Monk                        }
2116f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    } catch (IOException e) {
2126f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                        e.printStackTrace();
2136f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                    }
214602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                }
215602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
216602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        } catch (SocketException e) {
2176f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            Log.e(TAG, "Failed to start proxy server", e);
2186f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        } catch (IOException e1) {
2196f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            Log.e(TAG, "Failed to start proxy server", e1);
220602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
221602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
222602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mIsRunning = false;
223602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
224602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
2256f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    public synchronized void setPort(int port) {
2266f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        if (mCallback != null) {
2276f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            try {
2286f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                mCallback.setProxyPort(port);
2296f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            } catch (RemoteException e) {
2306f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                Log.w(TAG, "Proxy failed to report port to PacManager", e);
2316f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            }
2326f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        }
2336f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        mPort = port;
2346f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    }
2356f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk
2366f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    public synchronized void setCallback(IProxyPortListener callback) {
2376f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        if (mPort != -1) {
2386f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            try {
2396f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                callback.setProxyPort(mPort);
2406f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            } catch (RemoteException e) {
2416f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk                Log.w(TAG, "Proxy failed to report port to PacManager", e);
2426f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk            }
2436f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        }
2446f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        mCallback = callback;
2456f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    }
2466f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk
247602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public synchronized void startServer() {
248602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mIsRunning = true;
249602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        start();
250602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
251602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk
252602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    public synchronized void stopServer() {
253602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        mIsRunning = false;
254602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        if (serverSocket != null) {
255602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            try {
256602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                serverSocket.close();
257602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                serverSocket = null;
258602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            } catch (IOException e) {
259602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk                e.printStackTrace();
260602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk            }
261602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk        }
262602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk    }
2636f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk
2646f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    public boolean isBound() {
2656f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        return (mPort != -1);
2666f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    }
2676f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk
2686f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    public int getPort() {
2696f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk        return mPort;
2706f8a68f49a7e8cf86104e721a1e8be7568b5f730Jason Monk    }
271602b232a06ede86999aa362a12eb28cbc782dc1dJason Monk}
272